Friday, December 5, 2008
Sydney Groovy/Grails User Group
Monday, November 10, 2008
Grails WebTest Plugin 0.6 alpha
I've been working on committing some improvements to the Grails WebTest Plugin. You can download the alpha version here (I haven't yet released it to the plugin repository). I'd appreciate feedback from users with existing applications with non-trivial webtests regarding any regressions or upgrade issues they have. If all looks ok I will update the official repository version shortly.
Release Notes
setUp/tearDown at the method and class level
classSetUp() and classTearDown() are run as individual test cases. SetUp() and tearDown() are run as the first and last steps of each test case.
New superclass AutoWebTest
This new superclass will automatically run all methods starting with test. This saves you having to manually maintain the suite method unless you really want to for test order reasons.
AutoWebTest will also generate the test case name from the class and method name removing the need for repetitive webtest('blah'){...} code. The generated test name also makes it much easir to find the failing test from the generate reports.
MethodMissing code has been added so you can refactor a group of steps without having to wrap them in and ant.group closure.
You can now call config() as the first step in your test method to set WebTest options like host, port and ajax support
-nostart option allows you to runthe tests against a server that is already running. It should come after run-webtest on the command line
System parameters now passed through to WebTest. They need to be placed directly after grails on the command line e.g. grails -Dwt.headless=true run-webtest
-Dwt.headless=true to hide Swing monitor and stop browser launching
-Dserver.port=XXXX to get the tests to run against a server on a non-default port
The plugin has been updated with the latest WebTest release which includes an update of HtmlUnit to version 2.3
Application lib folder now on WebTest classpath. This avoids the need to duplicate/move libraries into webtest/home/lib
Custom steps
You can now extend com.canoo.webtest.steps.Step by placing groovy classes in webtest/tests/step. They will be automatically loaded at runtime and allow for easy testing of complicated scenarios such as JSON interfaces and email integration
The last project I worked on used these custom steps to start, check then stop an embedded Wiser SMTP server for testing email functionality.
Upgrade Instructions
delete plugins/webtest-0.x
svn delete webtest/home, commit.
This avoids svn issues as the install script deletes the folder and extracts the latest build over the top, removing the .svn directories
grails install-plugin grails-webtest-0.6.zip
You need to copy the zip file into the root folder of your project and run the command there.
Thursday, October 30, 2008
Open Source Developers Conference - Sydney Dec 2008
Wednesday, September 17, 2008
How to WebTest a site using an invalid SSL certificate
UPDATE: there is a option you can pass to the config step which should achieve the same thing: useInsecureSSL
A very short post regarding an issue I recently came across while testing an application I'm working on.
My application needs to interface with and existing PHP application that uses SSL. To acceptance test this functionality I am writing a WebTest that drives both applications to assert information is flowing correctly between them.
Unfortunately the test instance of the PHP application I have been given to use has a self-signed SSL certificate which causes WebTest to fail with a SSLHandshakeException.
To ignore the self signed certificate, add the following line to your test:
groovy('step.context.currentWebClientContext.webClient.useInsecureSSL = true')
Tuesday, August 26, 2008
Having trouble installing the Quartz plugin? Read on...
GrailsJobFactory.java:75: cannot find symbol symbol : constructor JobExecutionException(java.lang.String,java.lang.Exception) location: class org.quartz.JobExecutionException throw new JobExecutionException(e.getMessage(), e);It's caused by a library clash between the two plugins as JSecurity also ships with quartz.jar. Luckily (according to Les from the JSecurity project), JSecurity doesn't actually rely on that jar so you can delete it from plugins/jsecurity-x.x/lib which will solve the compile issue.
Monday, August 18, 2008
Grafton Hillclimb
View Larger Map
Tuesday, August 5, 2008
Jasper Reports Grails Plugin - sub-reports
$P{SUBREPORT_DIR} + "mysubreport.jasper"(assuming your subreport is in the same directory as the parent report and called mysubreport.jasper) Don't forget to give the parameter a default value of empty string so that it still works when previewing in iReport. You also need to remember that the plugin runs the compiled versions (*.jasper not *.jrxml) so you need to compile the reports with iReport before running the application.
Monday, August 4, 2008
Jasper Reports Grails Plugin Gotcha
java.lang.NullPointerException at net.sf.jasperreports.engine.JRPropertiesMap.readObject(JRPropertiesMap.java:185)
Thursday, July 24, 2008
Functional Test Driven Development with Grails and WebTest
Wednesday, July 23, 2008
Audi helps you time the lights!
Sunday, July 20, 2008
Full dyno day results
Saturday, July 19, 2008
Dyno Day Result
Wednesday, July 16, 2008
Switchable Grails DataSource
package com.leebutts import org.springframework.jdbc.datasource .lookup.AbstractRoutingDataSource import com.leebutts.Environment import org.springframework.context.ApplicationContextAware import org.springframework.context.ApplicationContext import javax.sql.DataSource import org.springframework.jdbc.datasource.DriverManagerDataSource class SwitchableDataSource extends AbstractRoutingDataSource implements ApplicationContextAware { def applicationContext public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext } protected DataSource determineTargetDataSource() { DriverManagerDataSource ds = super.determineTargetDataSource(); def env = EnvironmentHolder.getEnvironment() if (env && env.passwordRequired && ds) { ds.setPassword(env.password) } return ds } protected Object determineCurrentLookupKey() { def env = EnvironmentHolder.getEnvironment() return env?.id ?: Environment.list()[0]?.id } }SwitchableDataSource is the facade that delegates to the list of standard DriverManager data sources. They are defined in grails-app/conf/spring/resources.groovy using the environment settings from the Environment class. Here's my spring config in resources.groovy:
import com.leebutts.SwitchableDataSource import com.leebutts.Environment import org.springframework.jdbc.datasource.DriverManagerDataSource beans = { parentDataSource(DriverManagerDataSource) { bean -> bean.'abstract' = true; driverClassName = 'com.mysql.jdbc.Driver' username = "root" } Environment.list().each {env -> "${env.prefix}DataSource"(DriverManagerDataSource) {bean -> bean.parent = parentDataSource bean.scope = 'prototype' def port = env.port ?: 3306 url = "jdbc:mysql://${env.host}:${port}/switchingDemo" if (env.user) { username = env.user } if (env.password) { password = env.password } } } def dataSources = [:] Environment.list().each {env -> dataSources[env.id] = ref(env.prefix + 'DataSource') } dataSource(SwitchableDataSource) { targetDataSources = dataSources } }Environment currently uses a static list to hold the environment config. This could be done better via a reloadable properties file or by adding a view/controller to modify the environment settings on the fly. Environment.groovy:
package com.leebutts class Environment { static environments = [] static { environments << [id: 1, name: 'local', prefix: 'local', host: 'localhost'] environments << [id: 2, name: 'UAT', prefix: 'uat', host: 'uat.leebutts.com'] environments << [id: 3, name: 'Testing', prefix: 'testing', host: 'testing.leebutts.com'] environments << [id: 4, name: 'Beta', prefix: 'beta', host: 'beta.leebutts.com', passwordRequired: true] environments << [id: 5, name: 'Prod', prefix: 'prod', host: 'db.leebutts.com', user:'grails', port: 13306, passwordRequired: true] //unique id check environments.each {env -> assert environments .findAll {it.id == env.id}.size() == 1} } static list() { return environments } }SwitchableDataSource needs a way to determine which environment the current request wishes to use. It does this via a ThreadLocal holder EnvironmentHolder.groovy:
package com.leebutts class EnvironmentHolder { private static final ThreadLocal contextHolder = new ThreadLocal(); static void setEnvironment(Map environment) { contextHolder.set(environment); } static getEnvironment() { return contextHolder.get(); } static void clear() { contextHolder.remove(); } }Now that the infrastructure is in place, the next step is to add a controller to allow users to select the environment they wish to use and a filter to set a default environment if one has not yet been chosen. The controller is called via ajax (in my application) but could be used as a standard controller just as easily. It looks up the Environment based on an ID and then tests the connection to make sure the password supplied is valid (if a password is required as specified in the environment config). If a password is being used it takes a copy of the environment config map, adds the password and stores it in the session so that the user doesn't have to re-enter the password on every screen and so that only this user has access to the password. EnvironmentController.groovy:
import com.leebutts.Environment import com.leebutts.EnvironmentHolder import javax.servlet.http.HttpServletResponse import org.codehaus.groovy.grails.commons.ApplicationAttributes import org.codehaus.groovy.grails.web.context.ServletContextHolder class EnvironmentController { def change = { if (params.environment) { def env = Environment.list() .find {it.id == new Integer(params.environment)} if (env) { if (env.passwordRequired) { if (params.password) { //take a copy and add a pword env = addPasswordToEnvCopy(params, env) } else { render 'PASSWORD REQUIRED' response.setStatus( HttpServletResponse.SC_UNAUTHORIZED) return } } //test connection def oldEnv = EnvironmentHolder.getEnvironment() EnvironmentHolder.setEnvironment env def ds = getDataSourceForEnv() try { def con = ds.getConnection() session.environment = env render 'Environment change complete.' } catch (e) { EnvironmentHolder.setEnvironment oldEnv render 'Unable to connect to database: ' + e.message response.setStatus( HttpServletResponse.SC_UNAUTHORIZED) return } } else { render 'No such environment' response.setStatus( HttpServletResponse.SC_BAD_REQUEST) } } else { render 'Missing parameter environment' response.setStatus(HttpServletResponse.SC_BAD_REQUEST) } } private def getDataSourceForEnv() { def servletContext = ServletContextHolder.servletContext def ctx = servletContext .getAttribute( ApplicationAttributes.APPLICATION_CONTEXT) return ctx.dataSource } private Map addPasswordToEnvCopy(Map params, env) { def myEnv = [:] env.each {key, val -> myEnv[key] = val } myEnv.password = params.password return myEnv } }As mentioned, there is also a simple filter for defaulting the environment to the first one in the list if one has not been selected and storing it in the ThreadLocal holder. Filters.groovy:
import com.leebutts.EnvironmentHolder import com.leebutts.Environment class Filters { def filters = { all(uri: '/**') { before = { if (!session.environment) { session.environment = Environment.list()[0] } EnvironmentHolder.setEnvironment(session.environment) } } } }The final step is to add an environment selection form to your layout so that users can choose their environment. views/layouts/main.gsp:
<html> <head> <title>Administration System</title> <link rel="stylesheet" href="${createLinkTo(dir: 'css', file: 'main.css')}"/> <g:layoutHead/> <g:javascript library="application"/> <g:javascript library="prototype"/> <script type="text/javascript"> function refresh() { window.location.reload(false); } function loading() { document.getElementById('spinner').style.display = 'inline'; document.getElementById('error').style.display = 'none'; } function showError(e) { var errorDiv = document.getElementById('error') errorDiv.innerHTML = '<ul><li>' + e.responseText + '</li></ul>'; errorDiv.style.display = 'block'; } </script> </head> <body> <div class="logo"> <div style="margin-left:10px;"> <h1>Current Environment: ${session.environment?.name ?: 'None'}</h1> <form action=""> <g:select name="environment" from="${com.leebutts.Environment.list()}" optionKey="id" optionValue="name" value="${session.environment?.id}"/> <g:passwordField name="password"/> <g:submitToRemote value="Select" controller="environment" action="change" update="currentEnv" onLoading="loading();" onComplete ="document.getElementById('spinner').style.display='none';" onFailure="showError(e)" onSuccess="refresh()"/> <br/> <div class="errors" id="error" style="display:none;width:500px;"> </div> <img id="spinner" style="display:none;" src="${createLinkTo(dir: 'images', file: 'spinner.gif')}" alt="Spinner"/> </form> </div> </div> <g:layoutBody/> </body> </html>The finished screen looks something like this:
Tuesday, July 15, 2008
Unreal 3D Car Art
Sunday, July 13, 2008
New PB at Willowbank yesterday
I went out to Willowbank Raceway yesterday for some 1/4 mile fun. My best time was 13.791, about 1/2 a second quicker than last time thanks to a new exhaust, front mount intercooler and clutch.
I haven't found any video of my runs but here's some I took of the other VR-4s and Evos that were there. The last clip is of an R35 GT-R which ran an 11.3!