Monday, December 14, 2009

IntelliJ's IDEA Java/Groovy/Ruby/PHP/etc. IDE is considered by many to be the best IDE for Java/Groovy, there is a free version.

  • Download and install IntelliJ's IDEA v9.0+ CE from here.

  • Download the latest AribaWeb plugin for IDEA here.

    • md aribaweb\lib in IDEA's plugin directory

    • copy or move the plugin JAR into the above directory

  • Create a new AribaWeb project i.e. type aw and follow the prompts (e.g. see this video)

  • Launch IDEA at the end i.e. MyFirstApp.ipr

    • You'll be prompted to tell IDEA where AribaWeb lives

    • Point it to wherever you installed AiribaWeb

    • Adjust the Ant version to be used for your project after watching this video

    • You can adjust the JDK used by IDEA for your project under File | Project Structure | Project (aka General Settings for Project 'Whatever')

  • BTW, there's a little more info. here, including how to install the Eclipse plugin

Ariba made it big through the last dotcom boom/bust with procurement services via the web for B2B; they (quietly) open-sourced a web development framework (that they've developed continuously since 1999) last summer (AribaWeb) and now it's come to my notice - e.g. they claim 100 times less code than Rails, never mind JSF, Tapestry, Struts2, etc.

Live, in the browser editing of the presentation artifacts is one interesting feature; it's all based on JEE and has an ORM layer over top of Hibernate (something like Grails has i.e. GORM). Read all about it here.

P.S. Somehow Kirk beating the Gorm is still one of my favourite (cheesy) sci-fi scenes; how about you?

Wednesday, November 18, 2009

Location Services

Apple iPods offer a Location Service to programmers for use in applications; imagine being able to get a free yourOrg app from the Apple AppStore that, once logged-in, allowed you to get driving/parking/walking directions to your next class or meeting. The timetabling info. is already available from yourOrg apps.

Location Services uses (SkyHook's) amalgam of GPS (iPhone only), WiFi and cellphone tower triangulation (iPhone only). WiFi has the advantage of being about as accurate as GPS, it works well indoors and it works on both iTouch (~$200) and iPhone (~$500 or $150 +$65/m x 36).

Here is a (manual) webapp that allows you to upload your WiFi's MAC address, latitude & longitude to SyHook's databases.

Just add some programming and we're good to go!

Wednesday, November 4, 2009

Delirious

..my mind goes racing around and around and around it chases it's tail and starts all over again, endlessly. That's what the fever & delerium of this latest flu is like for me.

Now, that its passed, I draw your attention to yet another way we could, as a group of web-application developers, handle authentication & authorization for all those web applications we're sure to build before the next millenium.

This one differs from the one I concocted a few weeks ago, it's based on OpenSSO:

  • A&A is handled completely outside your app, you lockdown URLs by User, Role & Permission
    • It depends upon your using RESTful URLs, you get these out-of-the-box w/Grails
  • A&A is handled by Sun's OpenSSO product
  • Like the name says single-signon for all your web apps
  • You don't even have to write a login page
  • There is, of course, a Grails plugin

Here's some links to a bit of OpenSSO usage:


Fever time!

Thursday, October 29, 2009

Writing a Java applet in Grails


  • Install the plugin:
  • As per the reference, self-sign a certificate for yourself with these 2 command-line steps:
    • keytool -genkeypair -alias <alias> -storepass <password>
    • keytool -selfcert -alias <alias> -storepass <password>
      • where <alias> & <password> are values that you choose
      • this resulted in a .keystore file appearing in my home directory
  • Add this chunk to your conf/Config.groovy file:

    •   plugins {  
          applet {  
            //groovyJar = 'C:/groovy/groovy-1.6.0/embeddable/groovy-all-1.6.0.jar'  
            jars {  
              '/testApplet.jar' { // leading '/' is optional **didn't work for me until I added it though**  
              groovy = false; // set to false to skip including groovy-all-* in the jar  
              classes= ['ca.upei.cs.avc.applet.*.class', 'lib/classes111.jar', 'lib/LabDataViewer.jar', 'lib/ResultsLib.jar']  
              sign {  
                alias = 'alias' //your alias in keystore  
                storepass = 'password'//alternatively, use a system property with -Dstorepass=password  
              }  
              pack200 = true  
            }  
          }  
        }  
      

    • Above, the classes value contains the Java class name of my applet (to be written) and some JARs it needs at compile or runtime. Any number of applets can be listed in the jars stanza. Notice, you can reference the standard Grails lib directory so you don't need to repeat yourself i.e. copy them somewhere special for the applet(s).
  • My applet (i.e. src/java/whatever/applet/LabDataViewerApplet.java ) looks like this:

    •  package whatever.applet;  
         
          import javax.swing.JApplet;  
          import java.sql.*;  
         
          import oracle.jdbc.driver.OracleDriver;  
          import upei.avc.ds.LabDataViewer;  
         
          public class LabDataViewerApplet extends JApplet {  
         
           int year = 1899;  
           int labNumber = -1;  
           private Connection con;  
         
           public LabDataViewerApplet() {}  
         
           public void init() {  
            if (null != this.getParameter("labNumber")) {  
             labNumber = Integer.parseInt(this.getParameter("labNumber"));  
            }  
            if (null != this.getParameter("year")) {  
             year = Integer.parseInt(this.getParameter("year"));  
            }  
            try {  
             jbInit();  
            } catch(Exception e) {  
             e.printStackTrace();  
            }  
           }  
         
           private void jbInit() throws Exception {  
            this.getContentPane().setLayout(null);  
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());  
            con = DriverManager.getConnection(  
                                "jdbc:oracle:thin:@host:port:database",  
                                "id","password");  
            LabDataViewer viewer = new LabDataViewer(labNumber, year, con);  
            this.getContentPane().add(viewer, null);  
           }  
         
          }  
         
      

    • This applet will get a JDBC Connection to an Oracle instance and instantiate & attach Robert Page's LabDataViewer (Java Swing) app to it's contentPane; the (applet) containing page will pass in parameters to the viewer to customize the data/view.

    • You compile & package the applet by running this command:
      • grails package-applet
      • This should result in the file testApplet.jar being placed in your web directory e.g.

        • C:\projx\grails_apps\testApplet>grails package-applet
          Welcome to Grails 1.1.1 - http://grails.org/
          Licensed under Apache Standard License 2.0
          Grails home is set to: C:\tools\grails\grails-1.1.1
          
          Base Directory: C:\projx\grails_apps\testApplet
          Running script C:\Documents and Settings\default\.grails\1.1.1\projects\testApplet\plugins\applet-0.1\scripts\
          PackageApplet.groovy
          Environment set to development
           [groovyc] Compiling 1 source file to C:\Documents and Settings\default\.grails\1.1.1\projects\testApplet\classes
              [echo] Building web-app//testApplet.jar with the following Autojar arguments: [-o, web-app//testApplet.jar, -c, C:\Documents and Settings\default\.grails\1.1.1\projects\testApplet\classes, ca.upei.cs.avc.applet.*.class, lib/classes111.jar, lib/LabDataViewer.jar, lib/ResultsLib.jar]
          2009-10-20 16:00:33,299 [main] WARN  root  - Missing files:
             com/keyoti/rapidSpell/desktop/RapidSpellAsYouType.class
             org/jdesktop/layout/GroupLayout$Group.class
             org/jdesktop/layout/GroupLayout$ParallelGroup.class
             org/jdesktop/layout/GroupLayout$SequentialGroup.class
             org/jdesktop/layout/GroupLayout.class
             upei/avc/oracle/OraOCIConn.class
              [echo] pack200 is enabled, packing jar...
              [echo] Signing web-app//testApplet.jar as alias: alias
           [signjar] Signing JAR: C:\projx\grails_apps\testApplet\web-app\testApplet.jar to C:\projx\grails_apps\testAp
          plet\web-app\testApplet.jar as alias
           [signjar] Warning:
           [signjar] The signer certificate will expire within six months.
           [signjar] Enter Passphrase for keystore:
          C:\projx\grails_apps\testApplet>

        • Don't be concerned with the reported missing files above; in this case they were never used by the Swing application
        • AutoJar (used by the plugin) uses reflection to find the class & jar files required by the applet
  • The applet-containing webpage looks like this; any number of parameters can be passed into the applet (they're passed as Strings):

    • <html>
      <head>
       <script src="http://java.com/js/deployJava.js"></script>
      </head>
      <body>
      <script>
       var attributes = {
         name:'myApplet',
         codebase:'.',
         code:'whatever.applet.LabDataViewerApplet.class',
         archive:'testApplet.jar',
         width:500,
         height:500
       } ;
       var parameters = {fontSize:16, labNumber: 6, year: 2006};
       var version = '1.6' ;
       deployJava.runApplet(attributes, parameters, version);
      </script>
      </body>
      </html>

    • The resulting page appears blank (except for a 500x500 grey area I reserved for the applet) and the LabDataViewer launches in it's own window, closing the webpage kills the applet/application.
    • Brought to you thanks to AutoJar, deployJava.js and the Grails' Applet plugin!

Some day...

...I'd like to get Single-Signon working with Grails & jSecurity but these links indicate it might be slow going:

  • http://n2.nabble.com/SSO-with-Grails-JSecurity-Plugin-td1334030.html#a1334030

  • http://www.nabble.com/RE%3A-SSO-with-Grails-JSecurity-Plugin-p20035703.html

  • http://www.nabble.com/JSecurity----custom-SSO-implementation-ts20178301.html#a20178301

  • http://www.nabble.com/SSO-with-Grails-JSecurity-Plugin-td19996414.html

  • http://tramuntanal.wikidot.com/jsecurityplugin

  • http://asrijaffar.blogspot.com/2008/08/grails-jsecurity-plugin.html

  • http://skillsmatter.com/podcast/java-jee/exploring-the-power-of-jsecurity-in-grails

Grails application base: Nimble

Looks promising and not unlike what I've recently been hacking together.

Debugging Grails from NetBean v6.7


  • Start Grails in debug mode from the command-line : grails-debug run-app

  • In NetBeans

    • Set a breakpoint

    • Select Attach Debugger... from the Debug menu

      • Type the value 5005 into the port field of the ensuing dialog

      • Press OK





Grails HowTo: Administer & ReUse Users & Roles in cross-(DB)schema apps w/security

The following design enables the central management of application Roles & Users, by a Grails application, for other Grails applications and implements User authentication via LDAP (authorization is managed by Roles).

The code that follows can not only be used to implement the central CRUD of AppGroups, Apps, Roles & Users but can be repeatedly re-used in each application that desires to take advantage of these AppGroups, Apps, Roles & Users.

To start, create a Grails app from the command-line:


grails create-app AppGroupUserRoleAdmin
cd AppGroupUserRoleAdmin

or Ctrl-Shift-N/Groovy/Grails Application in the NetBeans 6.7 IDE.

Install plugins (note: the jSecurity project has been adopted by Apache and renamed to first Ki and now Shiro; change jsecurity to apache.shiro if using the shiro Grails plugin i.e. grails install-plugin shiro and import org.apache.shiro.authc.AccountException):


grails install-plugin ldap
grails install-plugin jsecurity

In this scheme, Users (of certain Applications) will have Roles (specific to each Application) and Applications will be classified by their Application Group membership. e.g. joe is a User of the Budget application, the Budget application is a member of the Finance application group.

Create some classes to model the security domain i.e. Users, Roles, Applications & Application Groups :


grails create-domain-class whatever.AppUser
grails create-domain-class whatever.AppRole
grails create-domain-class whatever.App
grails create-domain-class whatever.AppGroup

Edit the resulting domain objects like so:


  • AppUser:

package whatever

class AppUser {
/* NOTE:
* Declare the id & version attributes as type: Integer
* or else the Grails default type (Long) will be used.
* Trying to persist Java Long values as JDBC types bigint
* will currently fail as our Ingres DB doesn't support that type (yet).
*/
Integer id
Integer version
String name // used by LDAP authentication
Integer idNumber // possibly used in SQL?
String toString() { name + ":" + idNumber + ":" + roles }
static hasMany = [roles:AppRole]
static constraints = {
name(nullable: false, blank: false, unique: true)
idNumber(min: 1, unique: true)
roles(nullable: false)
}
static mapping = {
table 'gr8_appuser'
roles joinTable: 'gr8_appuser_role'
}
}
  • AppRole


package whatever

class AppRole {
/* NOTE:
* Declare the id & version attributes as type: Integer
* or else the Grails default type (Long) will be used.
* Trying to persist Java Long values as JDBC types bigint
* will currently fail as our Ingres DB doesn't support that type (yet).
*/
Integer id
Integer version
String name
static belongsTo = [app:App]
String toString() { name + ":" + app }
static constraints = {
name(nullable: false, blank: false, unique: true)
}
static mapping = {
table 'gr8_approle'
}
}
  • App

package whatever
class App {
/* NOTE:
* Declare the id & version attributes as type: Integer
* or else the Grails default type (Long) will be used.
* Trying to persist Java Long values as JDBC types bigint
* will currently fail as our Ingres DB doesn't support that type (yet).
*/
Integer id
Integer version
String name
String entryUrl
static belongsTo = [appGroup:AppGroup]
static hasMany = [roles:AppRole]

String toString() { name + ":" + entryUrl }
static constraints = {
name(nullable: false, blank: false, unique: true)
entryUrl(url: true, nullable: false, blank: false)
}
static mapping = {
table 'gr8_app'
}
}
  • AppGroup

package whatever

class AppGroup {
/* NOTE:
* Declare the id & version attributes as type: Integer
* or else the Grails default type (Long) will be used.
* Trying to persist Java Long values as JDBC types bigint
* will currently fail as our Ingres DB doesn't support that type (yet).
*/
Integer id
Integer version
String name
String toString() { name }
static hasMany = [apps:App]
static constraints = {
name(nullable: false, blank: false, unique: true)
}
static mapping = {
table 'gr8_appgroup'
}
}

The above security-centric objects will be persisted to a DB schema from which we will grant select rights to other DB schemas; this will allow for their (read-only) re-use by schema-specific applications.

At this point you should be able to run the project; this will create the tables via GORM/Hibernate.

Modify the Grails' BootStrap class file to auto-insert some records into the DB so that we can login upon startup (Note: If you're re-using this code in an application other than the global one you may not want to auto-create Roles & Users upon application startup, in which case you can skip this step):


import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
import groovy.sql.Sql
import javax.sql.DataSource

class BootStrap {

def DataSource dataSource

def init = { servletContext ->
def sql = new Sql(dataSource)
def version = 0

// User
def int userId = 0
def Integer adminIdNumber = CH.config.adminUserIdNumber
def String userName = CH.config.adminUserName
sql.execute("insert into gr8_appuser (id, version, id_number, name) values (${userId}, ${version}, ${adminIdNumber}, ${userName})")
// App group

def int groupId = -2
def String groupName = CH.config.adminAppGroupName
sql.execute("insert into gr8_appgroup (id, version, name) values (${groupId}, ${version}, ${groupName})")

// App
def int appId = -3
def String appName = CH.config.applicationName
def String entryUrl = CH.config.entryUrl
sql.execute("insert into gr8_app (id, version, app_group_id, entry_url, name) values (${appId}, ${version}, ${groupId}, ${entryUrl}, ${appName})")

// Role
def int roleId = -4
def String roleName = CH.config.adminRoleDescr
sql.execute("insert into gr8_approle (id, version, app_id, name) values (${roleId}, ${version}, ${appId}, ${roleName})")

// User/Role r'ship
sql.execute("insert into gr8_appuser_role (app_user_roles_id, app_role_id) values (${userId}, ${roleId})")
}


def destroy = {
}
}


We'll put the ID of the Administrator of this new User/Role/App/Group application we're creating into Grails' config file conf\Config.groovy, this User will be able to do CRUD for all the applications we'll build in the future; just add add a snippet to the (v 1.1.1) environments group i.e. so it looks like this after you're done (only add the applicationName line if you're re-using this code in a subsequent application):


environments {
production {
grails.serverURL = "http://www.changeme.com"
}
development {
grails.serverURL = "http://localhost:8080/${appName}"applicationName = "${appName}"
adminAppGroupName = "Admin Applications"
adminRoleDescr = "Administrator"
adminUserIdNumber = 666
adminUserName = "safe"
entryUrl = "http://localhost/${appName}"

Adjust the User & URL to taste, the User will need to be authenticated by LDAP.

As mentioned in previous blog postings, now we need to create a (jSecurity) realm for the purposes of User authentication i.e. AuthRealm:


package whatever

import javax.naming.AuthenticationException
import javax.naming.Context
import javax.naming.NamingException
import javax.naming.directory.BasicAttribute
import javax.naming.directory.BasicAttributes
import javax.naming.directory.InitialDirContext
import org.jsecurity.authc.AccountException
import org.jsecurity.authc.CredentialsException
import org.jsecurity.authc.IncorrectCredentialsException
import org.jsecurity.authc.UnknownAccountException
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH

/**
* Simple realm that:
* - authenticates users against an LDAP server
* - authorizes users against a DB.
*/
class AuthRealm {
static authTokenClass = org.jsecurity.authc.UsernamePasswordToken
def grailsApplication
def authenticate(authToken) {
if (authToken.username && authToken.password) {
List matches = getEntriesByCommonName(authToken.username)
if (matches && matches.size() == 1) {
LdapUserEntity user = getEntry(matches)
if (user.authenticate("" + authToken.password)) {
return authToken.username
} else {
java.lang.Thread.sleep(5*1000) // Wait for LDAP to reflect ACCOUNT LOCKOUT
user = getEntry(getEntriesByCommonName(authToken.username))
if ("TRUE".equals(user.lockedByIntruder)) {
throw new AccountException("The account is locked")
} else {
throw new IncorrectCredentialsException("Invalid password for user '${authToken.username}'")
}
}
} else {
throw new UnknownAccountException("No account found for user [${authToken.username}]")
}
}
}


def private List getEntriesByCommonName(String id) {
return GldapoSchemaClassForUser.findAll( filter: "(cn=" + id + ")" )
}

def private LdapUserEntity getEntry(List entries) {
if (entries && entries.size() == 1) {
return entries[0]
}
}

def hasRole(principal, roleName) {
def user = JsecUser.findByName(principal, [fetch:[roles:'join']])
if (user) {

return user.roles.any{
it.name == roleName &&
it.app.name == CH.config.applicationName
}
} else {
return false
}

}

def hasAllRoles(principal, roles) {
def user = JsecUser.findByName(principal, [fetch:[roles:'join']])
if (user) {
return user.roles.all {
it.name == roleName &&
it.app.name == CH.config.applicationName
}
} else {
return false
}
}
}

Create a class that will model the LDAP properties of Users i.e. .\grails-app\utils\LdapUserEntity:

package whatever

import gldapo.schema.annotation.GldapoNamingAttribute
import gldapo.schema.annotation.GldapoSynonymFor
import gldapo.schema.annotation.GldapoSchemaFilter

@GldapoSchemaFilter("(objectclass=person)")
class LdapUserEntity {

@GldapoNamingAttribute
@GldapoSynonymFor("cn")
String name

@GldapoSynonymFor("mail")
String email

@GldapoSynonymFor("uid")
String username

@GldapoSynonymFor("fullname")
String fullName

//@GldapoSynonymFor("pwdFailureTime")
//String passwordFailureTime
// it's an operational attribute, not sure how/what Groovy type to map it to

@GldapoSynonymFor("passwordExpirationTime")
String passwordExpirationTime

@GldapoSynonymFor("loginIntruderResetTime")
String loginIntruderResetTime

@GldapoSynonymFor("loginIntruderAttempts")
String loginIntruderAttempts

//@GldapoSynonymFor("loginIntruderAddress")
//String loginIntruderAddress
// it's a binary attribute, not sure how/what Groovy type to map it to

@GldapoSynonymFor("loginIntruderGraceLimit")
String loginIntruderGraceLimit

@GldapoSynonymFor("loginIntruderGraceRemaining")
String loginIntruderGraceRemaining

@GldapoSynonymFor("loginIntruderLimit")
String loginIntruderLimit

@GldapoSynonymFor("lockedByIntruder")
String lockedByIntruder
}

Create a controller i.e. AuthController; only Admins can login to this app, subsequent uses of this pattern by user-facing applications would likely only allow a Role e.g. User to login:


package whatever

import org.jsecurity.authc.AuthenticationException
import org.jsecurity.authc.UsernamePasswordToken
import org.jsecurity.SecurityUtils

class AuthController {

def jsecSecurityManager

def index = { redirect(action: 'login', params: params) }

def login = {
return [ username: params.username,
rememberMe: (params.rememberMe != null),
targetUri: params.targetUri
]
}

def signIn = {
def authToken = new UsernamePasswordToken(params.username, params.password)
if (params.rememberMe) {
authToken.rememberMe = true
}
try {
def subject = jsecSecurityManager.login(authToken)
if (subject.authenticated) {
if (jsecSecurityManager.hasRole(subject.getPrincipals(), "Administrator")) {
session.user = subject.principal
} else {
session.user = null
throw new AuthenticationException("No account found")
}
}
else {
session.user = null
throw new AuthenticationException("No account found")
}
def targetUri = params.targetUri ?: "/"
log.info "Redirecting to '${targetUri}'."
redirect(uri: targetUri)
}
catch (AuthenticationException ex){
// Authentication failed, so display the appropriate message
// on the login page.
log.info "Authentication failure for user '${params.username}'."
if (message(code: "account.locked").contains(ex.getMessage())) {
flash.message = message(code: "account.locked")
} else if (message(code: "account.unknown").contains(ex.getMessage())) {
flash.message = message(code: "login.failed")
}else {
flash.message = message(code: "login.failed")
}
// Keep the username and "remember me" setting so that the
// user doesn't have to enter them again.
def m = [ username: params.username ]
if (params.rememberMe) {
m['rememberMe'] = true
}
// Remember the target URI too.
if (params.targetUri) {
m['targetUri'] = params.targetUri
}
// Now redirect back to the login page.
redirect(action: 'login', params: m)
}
}
def signOut = {
// Log the user out of the application.
SecurityUtils.subject?.logout()
// For now, redirect back to the home page.
redirect(uri: '/')
}
def unauthorized = {
render 'You do not have permission to access this page.'
}
}

Create a page to login from i.e. views\auth\login.gsp:


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<title>Login</title>
</head>
<body>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:form action="signIn">
<input type="hidden" name="targetUri" value="${targetUri}" />
<table>
<tbody>
<tr>
<td>Username:</td>
<td><input type="text" name="username" value="${username}" /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password" value="" /></td>
</tr>
<tr>
<td>Remember me?:</td>
<td><g:checkBox name="rememberMe" value="${rememberMe}" /></td>
</tr>
<tr>
<td />
<td><input type="submit" value="Sign in" /></td>
</tr>
</tbody>
</table>
</g:form>
</body>
</html>

Don't forget to add an LDAP section to conf\Config.groovy or authentication will fail:



ldap {
directories {
directory1 {
defaultDirectory = true
url = "ldap://ldap.host.org"
base = "ou=otherUnit,o=org"
userDn = "cn=adminID,ou=unit,o=org"
password = "password"
searchControls {
countLimit = 40
timeLimit = 600
searchScope = "subtree"
}
}
}
schemas = [
LdapUserEntity
]
}

Adjust the LDAP location & credentials to taste.

Create some controllers for the domain objects:


grails create-controller whatever.App
grails create-controller whatever.AppGroup
grails create-controller whatever.AppRole
grails create-controller whatever.AppUser

Edit each controller to take advantage of Grails' scaffolding e.g.


package whatever

class AppUserController {

def scaffold = AppUser


}

Create a filter (e.g. conf\SecurityFilters.groovy) to secure the applications URLs:


class SecurityFilters {

def filters = {
loginCheck(controller: "*", action: "*") {
before = {
if (!session.user && actionName != 'login' &&
actionName != 'signIn')
{
redirect(controller:'auth', action:'login')
return false
}
}
}
}

}

Run the app (e.g. grails run-app)

Login with LDAPish credentials

Create AppGroups, Apps, Roles & Users for the applications you intend to build.


Re-use the above setup (minus the last 4 controllers, since you'll be using those objects in a read-only state anyway and won't need to do CRUD on them) for the applications you build in future and they'll be LDAP-authenticated and the Roles & Users will be centrally managed.


TODO:



  • Introduce the finer-grained control allowed by Permissions

  • Illustrate the use of jSecurity's GSP tags in markup pages

  • Refactor SQL code in BootStrap into a Service class, see: http://grails.org/doc/1.1.x/guide/single.html#8.3 Dependency Injection and Services

Issue a SQL GRANT command to expose the DB tables to other applications i.e. other DB schemas e.g. :


grant select on gr8_appuser to public
grant select on gr8_appuser_role to public
grant select on gr8_approle to public
grant select on gr8_app to public
grant select on gr8_appgroup to public

Using 2 Grails plugins to authenticate your applications' users

Persuant to my last blog post (Talking to LDAP from Grails) here is a follow-on post detailing the ridiculously easy process of using Grails' jSecurity & LDAP plugins to authenticate your application users. This example assumes your application will hand-off user authentication to LDAP and will handle authorization via filesystem or (more likely) database storage i.e. only store user IDs in your application's datastore, no passwords (yay!).

To begin:

  • create a grails app (e.g. grails create-app myApp

  • cd into the app's directory (e.g. cd myApp)

  • install the plugins e.g.

    • grails install-plugin ldap

    • grails install-plugin jsecurity


  • install a jSecurity LDAP realm e.g.

    • grails create-ldap-realm


  • install a jSecurity authenticating controller (i.e. the C in MVC, a servlet) e.g.

    • grails create-auth-controller


  • configure the LDAP plugin as per my previous post

As a result of the above steps you will now have the following additional/ or modified files in your Grails app's project tree:

  • grails-app

    • conf

      • Config.groovy


    • controllers

      • AuthController.groovy


    • realms

      • JsecLdapRealm.groovy


    • utils

      • GldapoSchemaClassForUser.groovy


    • views

      • JsecLdapRealm.groovy


    Now, the jSecurity script create-ldap-realm created the file JsecLdapRealm.groovy.

    The line:


    static authTokenClass = org.jsecurity.authc.UsernamePasswordToken

    signals jSecurity that this realm will participate in authenticating users; in fact, the authenticate() method is where this work is implemented (surprise!).

    By default the authenticate() method will have the typical Java/LDAP boilerplate code within it i.e.

     

    def authenticate(authToken) {

    log.info "Attempting to authenticate ${authToken.username} in LDAP realm..."

    def username = authToken.username

    def password = new String(authToken.password)

    // Get LDAP config for application. Use defaults when no config

    // is provided.

    def appConfig = grailsApplication.config

    def ldapUrls = appConfig.ldap.server.url ?: [ "ldap://localhost:389/" ]

    def searchBase = appConfig.ldap.search.base ?: ""

    def searchUser = appConfig.ldap.search.user ?: ""

    def searchPass = appConfig.ldap.search.pass ?: ""

    def usernameAttribute = appConfig.ldap.username.attribute ?: "uid"

    def skipAuthc = appConfig.ldap.skip.authentication ?: false

    def skipCredChk = appConfig.ldap.skip.credentialsCheck ?: false

    def allowEmptyPass = appConfig.ldap.allowEmptyPasswords != [:] ? appConfig.ldap.allowEmptyPasswords : true

    // Skip authentication ?

    if (skipAuthc) {

    log.info "Skipping authentication in development mode."

    return username

    }

    // Null username is invalid

    if (username == null) {

    throw new AccountException("Null usernames are not allowed by this realm.")

    }

    // Empty username is invalid

    if (username == "") {

    throw new AccountException("Empty usernames are not allowed by this realm.")

    }

    // Allow empty passwords ?

    if (!allowEmptyPass) {

    // Null password is invalid

    if (password == null) {

    throw new CredentialsException("Null password are not allowed by this realm.")

    }

    // empty password is invalid

    if (password == "") {

    throw new CredentialsException("Empty passwords are not allowed by this realm.")

    }

    }

    // Accept strings and GStrings for convenience, but convert to

    // a list.

    if (ldapUrls && !(ldapUrls instanceof Collection)) {

    ldapUrls = [ ldapUrls ]

    }

    // Set up the configuration for the LDAP search we are about

    // to do.

    def env = new Hashtable()

    env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.ldap.LdapCtxFactory"

    if (searchUser) {

    // Non-anonymous access for the search.

    env[Context.SECURITY_AUTHENTICATION] = "simple"

    env[Context.SECURITY_PRINCIPAL] = searchUser

    env[Context.SECURITY_CREDENTIALS] = searchPass

    }

    // Find an LDAP server that we can connect to.

    def ctx

    def urlUsed = ldapUrls.find { url ->

    log.info "Trying LDAP server ${url} ..."

    env[Context.PROVIDER_URL] = url

    // If an exception occurs, log it.

    try {

    ctx = new InitialDirContext(env)

    return true

    }

    catch (NamingException e) {

    log.error "Could not connect to ${url}: ${e}"

    return false

    }

    }

    if (!urlUsed) {

    def msg = 'No LDAP server available.'

    log.error msg

    throw new org.jsecurity.authc.AuthenticationException(msg)

    }

    // Look up the DN for the LDAP entry that has a 'uid' value

    // matching the given username.

    def matchAttrs = new BasicAttributes(true)

    matchAttrs.put(new BasicAttribute(usernameAttribute, username))

    def result = ctx.search(searchBase, matchAttrs)

    if (!result.hasMore()) {

    throw new UnknownAccountException("No account found for user [${username}]")

    }

    // Skip credentials check ?

    if (skipCredChk) {

    log.info "Skipping credentials check in development mode."

    return username

    }

    // Now connect to the LDAP server again, but this time use

    // authentication with the principal associated with the given

    // username.

    def searchResult = result.next()

    env[Context.SECURITY_AUTHENTICATION] = "simple"

    env[Context.SECURITY_PRINCIPAL] = searchResult.nameInNamespace

    env[Context.SECURITY_CREDENTIALS] = password

    try {

    new InitialDirContext(env)

    return username

    }

    catch (AuthenticationException ex) {

    log.info "Invalid password"

    throw new IncorrectCredentialsException("Invalid password for user '${username}'")

    }

    }

    Thankfully, because we're using GroovyLDAPObjects, this can be reduced to:


    def authenticate(authToken) {
    if (authToken.username && authToken.password) {
    List matches = getEntriesByCommonName(authToken.username)
    if (matches && matches.size() == 1) {
    GldapoSchemaClassForUser user = getEntry(matches)

    if (user.authenticate("" + authToken.password)) {
    return authToken.username
    } else {
    java.lang.Thread.sleep(5*1000) // Wait for LDAP to reflect ACCOUNT LOCKOUT
    user = getEntry(getEntriesByCommonName(authToken.username))
    if ("TRUE".equals(user.lockedByIntruder)) {
    throw new AccountException("The account is locked")

    } else {
    throw new IncorrectCredentialsException("Invalid password for user '${authToken.username}'")
    }
    }

    } else {
    throw new UnknownAccountException("No account found for user [${authToken.username}]")
    }
    }
    }
    def private List getEntriesByCommonName(String id) {
    return GldapoSchemaClassForUser.findAll( filter: "(cn=" + id + ")" )
    }

    def private GldapoSchemaClassForUser getEntry(List entries) {
    if (entries && entries.size() == 1) {
    return entries[0]
    }

    }


    What the above code is doing is:


    • if you passed me an ID & password then

      • lookup the ID in LDAP

      • iff there is 1 matching entry in LDAP then

        • get that entry in the form of my GLDAPO Schema object

        • try authenticating to LDAP as that user

        • if authenticated then

          • return the user name and we're done


        • else

          • wait 5 seconds

          • check LDAP for the same user

            • if the account is now locked out because of bad passwords then

              • throw an account-locked exception


            • else

              • throw a login-failure exception


      • else throw an account-not-found exception


Much briefer and easier to grok 8 months after you wrote it...

The only modification I made to the controller was to distinguish between a password failure and an account lockout so as to inform the user appropriately:


catch (AuthenticationException ex){
// Authentication failed, so display the appropriate message
// on the login page.
log.info "Authentication failure for user '${params.username}'."
if (message(code: "account.locked").contains(ex.getMessage())) {
flash.message = message(code: "account.locked")
} else {
flash.message = message(code: "login.failed")
}

and I modified the grails-app/i18n/jsecurity.properties message bundle to hold the message-to-the-user


account.locked = The account is locked, please try again later

All that remains to be done is to authorize the user e.g. do a DB check for the user ID, etc.

Finally, implement your view & business logic (only!) using jSecurity to restrict the views & business logic services to users with the appropriate roles and/or permissions. Did I blog that yet?

OMG - It Just Works!? or HowTo talk to LDAP from Grails

I was shocked (what, again!?) to find talking to LDAP from Grails so easy; typical Java/LDAP code looks like this, where the code marked as bold actually performs tasks related to the business purpose of the method:
package com.example.dao;
public class TraditionalPersonDaoImpl implements PersonDao {
  public List getAllPersonNames() {
     Hashtable env = new Hashtable();
     env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
     env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=example,dc=com");

     DirContext ctx;
     try {
        ctx = new InitialDirContext(env);
     } catch (NamingException e) {
        throw new RuntimeException(e);
     }

     LinkedList list = new LinkedList();
     NamingEnumeration results = null;
     try {
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        results = ctx.search("", "(objectclass=person)", controls);

        while (results.hasMore()) {
           SearchResult searchResult = (SearchResult) results.next();
           Attributes attributes = searchResult.getAttributes();
           Attribute attr = attributes.get("cn");
           String cn = (String) attr.get();
           list.add(cn);
        }
     } catch (NameNotFoundException e) {
        // The base context was not found.
        // Just clean up and exit.
     } catch (NamingException e) {
        throw new RuntimeException(e);
     } finally {
        if (results != null) {
           try {
              results.close();
           } catch (Exception e) {
              // Never mind this.
           }
        }
        if (ctx != null) {
           try {
              ctx.close();
           } catch (Exception e) {
              // Never mind this.
           }
        }
     }
     return list;
  }
}
That's a lot of noise around actually_doing_something, not to mention the (not shown) behind-the-scenes config'ing necessary to make it work.
Here are the steps in Grails:
  1. From the command-line inside your grails application do:

    1. grails install-plugin ldap

    2. the above command installs the Grails LDAP plugin which is based on the GroovyLDAPObject project which is based upon the Spring LDAP library


  2. Add something like this to the standard Grails file grails-app/conf/Config.groovy


    1. ldap {
         directories {
             directory1 {
                 defaultDirectory = true
                 url = "ldap://ldap-host.domain"
                 userDn = "cn=someLDAPAdminID,ou=myOU,o=myOrg"
                 password = "someLDAPAdminIDPassword"
                 searchControls {
                     countLimit = 40
                     timeLimit = 600
                     searchScope = "subtree"
                 }
             }
         }
      
         schemas = [
             GldapoSchemaClassForUser
         ]
      
      }

  3. Create a Groovy class to represent an LDAP entity e.g. in this case a user of our application i.e. utils/GldapoSchemaClassForUser.groovy

    1. import gldapo.schema.annotation.GldapoNamingAttribute
      import gldapo.schema.annotation.GldapoSynonymFor
      import gldapo.schema.annotation.GldapoSchemaFilter
      
      @GldapoSchemaFilter("(objectclass=person)")
      class GldapoSchemaClassForUser {
        
         @GldapoSynonymFor("cn")
         String name
        
         @GldapoSynonymFor("mail")
         String email
        
         @GldapoSynonymFor("uid")
         String username
        
         @GldapoSynonymFor("fullname")
         String fullName
      }


    2. the schema filter annotation forces searches to only return LDAP entities of a particular type

    3. the synonym annotation allows you to map an LDAP entity's attributes to Groovy object attributes of your own choice

    4. only the cn attribute needs to be in this class


Sorry, that's all there is to it.

To list a bunch of LDAP persons (e.g. whose cn start with mi) you can use code like this in a Grails controller or service class:
List matches = GldapoSchemaClassForUser.findAll(
     directory: "directory1",
     base: "ou=someOU,o=someOrg",
     filter: "(cn=mi*)"
 )

I intend to combine this LDAP plugin usage with Grails' jSecurity plugin to authenticate users to our Grails apps, perhaps combining it all into a plugin for our developers to use like so:
grails install-plugin ourShop_authc
Note: the LDAP configuration can also be done programmatically so the LDAP Admin credentials could be pulled from a DB (e.g. in conf/Bootstrap.groovy) if desired.

Are You Done Yet: Part Deux - Executable Specifications

I realized after building a bunch of executable specifications that they were pretty programmer-ish; who in their right mind would calculate intermediate values for a long, involved calculation and list them in a big table? A programmer would, that's who.

So, after re-evaluating my exec-specs (and reading these books) I crafted a bunch of end-to-end calculations that check the intermediate values as they go along - so that a casual observer, or even one brandishing a calculator (or a pencil and paper!) could follow along and grok the results.

These calculations are basically User Stories and they are the perfect complement to executable specifications; the User Stories relate business rules, their application and the value to the customer without going into details whereas the ex-specs dive right into all the gory details in pursuit of fleshing out the business rules, their general application and the edge cases therein.

They are a powerful combination to facilitate discussion and discovery of how the system is going to work and how to verify that it does. Forever.

Not to mention a great source of system documentation for both customers and developers.


HowTo: Build NetBeans projects for multiple environments

Sometimes you'll need to build a project out of NetBeans for different environments (e.g. your local Windows box or that Linux test server) but certain project files will need different values (e.g. file paths, DB credentials) for each environment.

Here's how to do that without much fuss or inconvenience:

  • create a conf directory in the project root (nb: conf can be whatever value you like better)

    • create a directory, for each environment you intend to build for, under the conf directory

      • e.g. dev, test, demo, prod


    • into each of these leaf directories create a file called build.properties

      • populate each build.properties file with environment-specific values


        • e.g. jdbc_url=jdbc:derby:myTestDB


  • modify the existing build.xml file located in the root of your NetBeans project, adding 2 XML-stanzas/ant-targets:

    • this one (i.e. -pre-compile) causes NetBeans to prompt you every time you build with a dialog that offers you the environment to build for, the default is settable and the dialog is easily dismissed by a click or an Enter keystroke:



      • <target name="-pre-compile">
        <input message="Choose a build type:"
        validargs="dev,demo,test,prod"
        addproperty="build.type"
        defaultvalue="dev"
        />
        <property file="conf/${build.type}/build.properties"/>
        </target>

    • this one (i.e. -post-compile) causes NetBeans to do whatever you want e.g. string replacement in a file e.g. web.xml:




      • <target name="-post-compile">
        <replace file="${build.web.dir}/WEB-INF/web.xml" token="@log_dir@" value="${log_dir}" />
        </target>


      • e.g. above, the value of log_dir will be taken from e.g. conf\prod\build.properties and will globally replace the token @log_dir@ in the file web\WEB-INF\web.xml, relative to the NetBeans project's root, when you select a prod build.


  • Lastly, complete any setup e.g. modify the targets of any string replacements

Buld your project and you should be prompted for a target environment.

NB: file paths in the build.properties are subject to the usual Java gotchas wrt/Window path separators i.e.

  • this: log_dir=C:\tmp\myProject\logs

    • will become this: C:tmpmyProjectlogs

    • after string replacement


  • the usual workarounds are available e.g.

    • use this instead: log_dir=C:\\tmp\\myProject\\logs

    • or this works too: log_dir=C:/tmp/myProject/logs


Build on it!

Writing to/from a filesystem to/from a DB via Groovy

I continue to be amazed by the brevity & clarity of the Groovy language; I produced a bunch of image files from an MS Access DB with this 4-line Groovy script:

def sql = groovy.sql.Sql.newInstance("jdbc:odbc:myDSN", '', '')
sql.rows('SELECT Image_Key, Blob_Segment FROM IMAGE').each { row ->
new File("C:/tmp/pics/${row.Image_Key}.jpg").withOutputStream {OutputStream out ->
out << row.Blob_Segment
}
}

after creating an ODBC User DSN (i.e. myDSN) that pointed to the *.mdb file.

If you get an OutOfMemory error message you can simply set JAVA_OPTS from the command-line (i.e. set JAVA_OPTS="-Xmx1536m") before calling the script i.e. groovy myScript.groovy

R u dONE yET?

Wouldn't it be great if we could simply write down what the user wants out of a software system and then run these requirements against the develop/ing/ed system (we'd know we were done when all the tests passed)?

If you think so too, read on.

I have recently done just this in hopes of moving forward the Pension Calculator project, we're stuck at the point of: "Are these results (in the pretty GUI) accurate (enough)?".

Imagine a system that uses a bunch of inputs to produce a couple of ranges of results; how/who should verify the results? In a fully-funded operation we'd have Business Analysts, Quality Assurance testers, Developers, Product Owners, etc. But we're not, so get over it already; someone has to desk-check the computations to verify that the results are correct. I tried writing Unit Tests, which was fine, but they're a little obscure to business customers - all that code! (Not to mention all the time & effort required to get a customer to pore over all those details with you) So, in an effort to prove the veracity of the code (to the customer) I turned to FitNesse.

Fitnese is (according to it's One Minute Description):

  • a tool for enhancing collaboration in software development

  • a lightweight, open-source framework that makes it easy for software teams to:

    • collaboratively define Acceptance Tests -- web pages containing simple tables of inputs and expected outputs

    • run those tests and see the results

  • a wiki; you can easily create and edit pages

  • a webserver, it requires neither configuration nor setup

(There is also a 2 Minute Example if you're interested)

The tests are convertable (by FitNesse) to/from spreadsheets to ease the transition between requirements (i.e Business Analyst) and tests (i.e. QA Tester) and are written in a natural language style e.g. given a user identified by

Bob with password D'oh! he can retire between 2010 and

2021


You can run the FitNesse tests from a command-line (given a tool like wget, curl, etc.) too, so it would not be difficult to integrate the test results into a build or Continuous Integration process.


Go fast!


Simple Sloppy Semantic Database...

...aka S3DB is a distributed infrastructure that relies on Semantic Web concepts for management of heterogeneous data, as is described in this Wikipedia article.

An open-source implementation (in PHP) is available, the rationale, core data model, and usage are described and illustrated here. There's even a video.

This development may impact the future management of bioinformatics.

An extension to JSON for S3DB is proposed here.

Push content to Rich_Internet_Apps

Happened upon this this morning: Lightstreamer

I'm sure there are more (Comet?) products like it out there...

In related news (to me), this entry at Wikipedia:

A long-polling Comet transport can be created by dynamically creating script elements, and setting their source to the location of the Comet server, which then sends back JavaScript (or JSONP) with some event as its payload. Each time the script request is completed, the browser opens a new one, just as in the XHR long polling case. This method has the advantage of being cross-browser while still allowing cross-domain implementations.

and...

An applet can open a raw TCP socket to the server from which it has loaded. This socket can remain open as long as the browser is at the document hosting the applet. Event notifications can be sent in any format — text or binary — and decoded by the applet.

and, from an ICEfaces page, also @Wikipedia:

ICEfaces Framework

The Framework is an extension to the standard
JSF framework, with the key difference in ICEfaces relating to the rendering phase. In standard JSF, the render phase produces new markup for the current application state, and delivers that to the browser, where a full page refresh occurs. With the ICEfaces framework, rendering occurs into a server-side DOM and only incremental changes to the DOM are delivered to the browser and reassembled with a lightweight Ajax Bridge. This results in seamless, smooth update of the browser page with only the necessary presentation elements being rerendered. The ICEfaces Framework also provides complete run-time management of Ajax Push using the server-initiated rendering APIs, and integrates that mechanism seamlessly with the JSF lifecycle.


Yet Another New Grails plugin

There are countless plugins for Grails; here's an interesting JavaScript library that focuses on data visualizations in the browser, now available as a Grails plugin...

Note: the demos don't work for me in FireFox 3.0 but they do in IE7 (WinXP).

An interesting paper on cushion treemaps is linked to from the author's blog.

Java (in)FAQ

see here

Browser-side DB

Way back in '05 at JavaOne the use of browser-side (Java) DB (Derby) was demo'd, a browser's JavaScript has access to local Java classes through the next-gen browser plugin so it can access DBs, webservers, etc. via the applet's classpath. The author's blog is here, it has some interesting topics e.g. server-side push to a web client that has local storage in Derby. See also this article on synchronizing an offline DB with an online DB, or this one - embedding a webserver into the browser.

Could we utilize this idea and offer clients an offline-mode usage pattern for our SmartClient-enable browser-apps?


P.S. Here is a NetBeans-specific tutorial on Derby

Launch & Attach from within Eclipse

If you've got the Grails Eclipse plugin you can stay in the IDE to launch the GWTShell by entering run-gwt-client into the dialogue box that is presented upon pressing said plugin's launcher.


Then, you can attach Eclipse (as specified in  a previous comment) and start debugging.


Eclipse is a lot slower than the DOS shell & Notepad but debugging can be pretty handy...


Next up, how to debug SmartGWT making a call into a Groovy/Grails service that returns JSON formatted data.

Remotely debugging Grails/SmartGWT from Eclipse


  • Edit %HOMEPATH%\.grails\%GRAILS_VERSION%\projects\%PROJECT_NAME%\plugins\gwt-%GRAILS_GWT_PLUGIN_VERSION%\scripts\RunGwtClient.groovy such that:

gwtRun('com.google.gwt.dev.GWTShell') {
        // Hosted mode requires a special JVM argument on Mac OS X.
if (antProject.properties.'os.name' == 'Mac OS X') {
jvmarg(value: '-XstartOnFirstThread')
}

arg(value: "-out")
arg(value: gwtOutputPath)
arg(value: "http://${targetServer}/${grailsAppName}")
}

becomes:

gwtRun('com.google.gwt.dev.GWTShell') {
        // Hosted mode requires a special JVM argument on Mac OS X.
if (antProject.properties.'os.name' == 'Mac OS X') {
jvmarg(value: '-XstartOnFirstThread')
}

jvmarg(value: '-Xdebug')
jvmarg(value: '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044')

arg(value: "-out")
arg(value: gwtOutputPath)
arg(value: "http://${targetServer}/${grailsAppName}")
}

  • Import your Grails/GWT project into Eclipse and create a Debug launch config (as blogged here)

  • Launch the GWTShell in debug-able mode with: grails run-gwt-client

  • Now, attach the Eclipse debugger to the GWTShell (as blogged here) and manipulate your Grails/SmartGWT application with the GWTBrowser (only) hitting Eclipse'd breakpoints as you go.

Of course, every time you run that Grails target i.e. run-gwt-client you'll be launching in debug-able mode; a smoother way to flag your intention to debug awaits your Groovy skills.

Remote debugging of any JVM...

...is possible if you start that JVM with these arguments:

  • -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044

e.g. a Blackberry or a Grails/SmartGWT app.

Of course, it helps to have the source code available so you can hit breakpoints, etc.in your favourite IDE.

I wouldn't say that Eclipse is my favourite IDE, but try this:

  1. Create a new Java Project in Eclipse IDE e.g. name it: test_remote_debugging

  2. Create a new main class e.g. RunMe

e.g. something like:


public class RunMe {
public static void main(String[] args) {
System.out.println("RunMe has started...");
HelperClass helper = new HelperClass();
for (int i = 0; i < 10; i++) {
System.out.println("RunMe is tickling the Help...");
System.out.println("Help is in a state i.e. tickled = " + helper.property);
helper.property++;
}
}
}

and here's the helper class.:


public class HelperClass {
public int property = 0;
public HelperClass() {}
public void tickleMe() { property++; }
}

Eclipse auto-compiles this source as you save it so the java.class files are already under your Eclipse workspace's %project_name%/bin directory (assuming you accepted all the defaults when creating the Eclipse Java project); open a command prompt there and issue this command to run your main class such that it will start & wait for a debugger to attach to (the JPDA default) port 1044:

  • java -cp . -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044 RunMe

You can verify that the JVM is listening by:

  • watching the response to the command above, it should look like:

    • Listening for transport dt_socket at address: 1044

  • running from another command prompt the following:

    • netstat -a | findstr 1044

    • expect output like so:

      • TCP your_host:1044 your_host:0 LISTENING

  • running from another command prompt the following:

    • telnet localhost 1044

    • expect output like so:

      • Connecting To localhost...

      • screen goes blank

    • Press the Enter key a few times to escape the TCP conversation

      • The output at the JVM's stdout will look something like this:



        • Debugger failed to attach: handshake failed - received >


          < - excepted >JDWP-Handshake<


        • see here for more info. about the Java Debug Wire Protocol


  • connect Eclipse to the JVM by doing the following:

    • Select from Eclipse's Run menu the Debug configurations... option

    • Select from the left pane of the ensuing dialogue the 'Remote Java Application' configuration type

    • Click on the 'New launch configuration' icon at the top left of the left pane...

    • Give the config. a new name or leave the default

    • Select a different project if the default is NOT the one you want

    • Default Connection type of: Standard (Socket Attach) is the one you want

    • Host should be localhost for this example but could be any host (this is where it helps to have the remote host JVM's source code at hand)

    • Port should be what you used in the java.exe command line to start the remote JVM

    • Press Apply to save this config. and then press Debug if you want

      • It helps if you've set a breakpoint in the source viewed in Eclipse or debugging isn't going to do much for you at this point

        • hint: create a breakpoint in Eclipse by double-clicking in the left-gutter of a source code view


    • If you didn't jump straight into debugging in the last step, set a breakpoint or two in Eclipse now

    • To debug the remote JVM, select the named launch configuration from the Eclipse dropdown menu that appears to the right of the 'bug' icon, just under the Refavtor menu in Eclipse v3.4

      • you should now be in, or prompted to be in, Eclipse's Debug perspective with your first breakpoint highlighted in the source view

      • you'll know you've screwed up something in your launch config.if you get a dialogue box that says something like:



        • 'Launching RunMe' has encountered a problem. Failed to connect to remote VM. Connection refused.


For more info on JPDA see also:

Grails' internals documentation

Someone has started an attempt (community-driven) to document the internal workings of the Grails stackhere

SmartClient GWT links

SmartClient Dev's Blog

Here's an interesting discussion of dynamically created smDataSources - the data-provider to all SM widgets e.g. Grids, Lists, Trees, dropdowns, Calendar(? haven't used it yet) - by an experienced user of SmartClient (using an ASP backend, so his team needs to replicate the server-side of SmartClient {Isomorphic only provides the server-side in WAR form, and for $$$ - go figure!?})

Links from that blog entry:

Setting up the Eclipse IDE w/Maven for SmartGWT

Getting started with Grails & SmartGWT

Read about the potential benefits of using the SmartGWT web GUI library here, here, here, here & here.


I believe, coupled to Groovy/Grails, this could be a killer productivity/maintenance win.


Here are some getting started instructions I gleaned from a Isomorphic SmartClient GWT forum thread:



  1. grails create-app test

  2. cd test

  3. grails install-plugin gwt

  4. grails create-gwt-module helloworld

  5. grails create-gwt-page helloworld/index.gsp helloworld

    1. Reply 'y' for "HelloworldController does not exist - do you want to create it now? [y/n]"



  6. Put SmartGWT statements into .\src\gwt\helloworld.gwt.xml

    <inherits name="com.smartgwt.SmartGwt"/>

    <inherits name="com.smartclient.theme.enterprise.Enterprise" />

  7. Put the workaround (described here) into the HTML HEAD section of grails-app\views\helloworld/index.gsp

    <script>

    var isomorphicDir = "${createLinkTo(dir: 'gwt/helloworld/sc/')}";

    </script>

  8. Copy the SmartGWT jar (i.e. smartgwt-1.3.jar) to lib/gwt e.g.

    1. md .\lib\gwt

    2. copy \tools\smart-gwt\smartgwtee-1.2\lib\smartgwt-1.3.jar .\lib\gwt\



  9. Copy the SmartGWT jar to your %GRAILS_HOME%/lib too (or Grails will fail when compiling) e.g.

    1. copy \tools\smart-gwt\smartgwtee-1.2\lib\smartgwt-1.3.jar %GRAILS_HOME%\lib\



  10. Compile GWT module with

    1. grails compile-gwt-modules

      --ignored warning:-------------------

      Warning, target causing name overwriting of name default

      [echo] Compiling GWT modules

      [echo] Module: helloworld

      [java] WARNING: 'com.google.gwt.dev.GWTCompiler' is deprecated and will be

      removed in a future release.

      [java] Use 'com.google.gwt.dev.Compiler' instead.

      [java] (To disable this warning, pass -Dgwt.nowarn.legacy.tools as a JVM arg.)

      ------------------:-------------------



  11. Test/run under GWT Shell with

    1. grails run-gwt-client

      1. This will launch the GWT Shell and the hosted GWT Browser

        1. The GWTShel will display the message: Starting HTTP on port 8888

        2. The GWT browser (launched by the Shell) wil display a HTTP 404 ERROR

          1. This is normal since the URL is incorect i.e. http://localhost:8080/test/

            1. Peek into "%HOMEPATH%\.grails\%GRAILS_VERSION%\projects\test\plugins\gwt-0.3.2\scripts\RunGwtClient.groovy" for clues:

              1. // The user can specify a host, a port, or both...

                1. grails run-gwt-client localhost:8888 launches the browser to http://localhost:8888/test/ which is closer to correct



              2. arg(value: "http://${targetServer}/${grailsAppName}")

                1. grailsAppName could be susbstituted (for what?) but I don't know the consequences of doing so. TBA







          2. The correct URL i.e. http://localhost:8888/helloworld/

            1. This page appears to be empty because the GWT entry point currently does nothing - it's empty

            2. If you right-click and choose View Source you'll see some content like this:


              1. <html><head>

                <script language='javascript' src='helloworld.nocache.js'></script>
                </head><body>

                <iframe src="javascript:''" id='__gwt_historyFrame' style='position:absolute;width:0;height:0;border:0'></iframe>
                </body></html>






          3. See step 13 below to rectify these results









  12. Run grails

    1. grails run-app

      (but something is wrong with the skin...)

    2. additional step (from SmartGWT forum posting) to correct this:

      1. rename grails-app/views/layouts/main.gsp.



    3. grails run-app

      (now it's OK)



  13. The steps above will produce a working example but it produces an empty page as a result!?

    1. Modify src\gwt\client\helloworld.java as below to put a GUI widget into the page (recompile before trying again):





package client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class helloworld implements EntryPoint {

public void onModuleLoad() {
final Button button = new Button("Click me");
final Label label = new Label();

button.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
if (label.getText().equals(""))
label.setText("Hello World!");
else
label.setText("");
}
});
RootPanel.get("slot1").add(button);
RootPanel.get("slot2").add(label);

}
}

Alternatively, you could put a SmartGWT widget in there instead e.g.



import com.smartgwt.client.util.SC;
...
public void onModuleLoad() {
SC.say("Hello SmartGWT"); // This launches SmartGWT's version of an alert dialogue box
}
// NOTE" No need to add this component to the Root Panel!
}

Next steps:


Compression for a project's CSS & JS

Nice; the JAWR tool allows you to develop CSS & JS in a plethora of files/directories but deploy all as a single file - hence, quicker loading for the client.

(Too many) Cool Grails plugins

>
  • PDF: Generate PDFs and send them to the browser by converting existing pages in your application to PDF on the fly; if processing fails HTML is returned in it's stead.
  • Searchable: Brings rich search features to Grails applications with minimum effort, and gives you power and flexibility when you need it
  • LiquiBase Database Refactoring: LiquiBase is a database change tracking tool. Major functionality includes:
    • 34 Refactorings
    • Extensibility to create custom refactorings
    • update database to current version
    • rollback last X changes to database
    • rollback database changes to particular date/time
    • rollback database to "tag"
    • Stand-alone IDE and Eclipse plug-in
    • "Contexts" for including/excluding change sets to execute
    • Database diff report
    • Database diff changelog generation
    • Ability to create changelog to generate an existing database
    • Database change documentation generation
    • Ability to save SQL to be applied for approval by a DBA
    • DBMS Check, user check, and SQL check preconditions
    • Can split change log into multiple files for easier management
    • Support for 10 database systems
  • JMS: Allows you to expose services and/or particular service methods as JMS listeners. It also provides a service and convenience methods on controllers and services for sending messages.
  • Mondrian is an open source Relational OnLine Analytical Processing (OLAP) engine that enables self-service analysis and reporting for users. Data is retrieved from a generic star-schema database so that any variable can be compared by any variable.

  • LifeRay: inherits Portlets Plugin, and adds Liferay Portal specific configurations and adapters.

  • JasperGrails: JasperReports on Grails; PDFs and charts too!

Of note to those who follow

Some links to interesting/useful (to me, maybe you too) things:

  • small and big icons
  • simple-to-use, powerful security API for your JVM apps, web & desktop, with single-signon, role-based and object and per-instance capabilities; there's a Grails plugin too, here's a GSP tag in action (sorry about the formatting!) :
    • <jsec:hasRole in="['Administrator', 'Supervisor', 'User']">
    •         <span class="menuButton">                  
    •                 <g:link controller="user" action="list">Manage Users</g:link>                 
    •         </span>             
    • </jsec:hasRole>
  • simple-to-use, powerful Swing GUI testing with Fest aka TestNG-Abbot
  • build Swing(plain, X and SwingSet) apps declaratively w/Groovy's Griffon (here and here and here) and test 'em w/Fest
  • using Groovy, JPA, TopLink and Java DB to persist a desktop application's UI state
  • Groovy on Seam and Gracelets and Seam - a DSL for Facelets and Groovy with easy integration
  • more Groovy stuff:
    • a demonstration of creating a (Swing) table binding dynamically and baking automatic sorting across multiple columns right into the (domain) model
    • scripting w/Groovy & bash to multiply developer productivity
  • counting characters in Java wrt/Unicode
  • Isomorphic SmartClient's MenuBar & MenuItem JS classes
  • Grails plugins:

I'm currently researching how best we can build and re-use menus in Grails webapps that are derived from DB values pertaining to structure, functionality and per-user security roles.