initial version
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0636388
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+Groovy examples
+===============
+
+This is a repository of Groovy source files using common (at some point) libraries.
+It is expected that all the source files at least compile with the current version of Groovy.
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..7b54e45
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,34 @@
+plugins {
+//    id 'com.github.spotbugs' version '1.6.2'
+    id "com.github.ben-manes.versions" version '0.20.0'
+}
+apply plugin: 'groovy'
+apply plugin: 'idea'
+
+repositories {
+    mavenCentral()
+    maven { url 'https://repository.jboss.org/nexus/content/groups/m2-release-proxy/' }
+    maven { url 'https://oss.jfrog.org/oss-snapshot-local/' }
+}
+
+dependencies {
+    compile "org.codehaus.groovy:groovy-all:$groovyVersion"
+
+    compile "org.apache.lucene:lucene-core:$luceneVersion"
+    compile "org.apache.lucene:lucene-analyzers-common:$luceneVersion"
+    compile "org.apache.lucene:lucene-queryparser:$luceneVersion"
+    compile "org.eclipse:osgi:$eclipseOsgiVersion"
+    compile("commons-httpclient:commons-httpclient:$commonsHttpClientVersion") {
+        exclude(module: 'junit')
+        exclude(module: 'commons-logging')
+        exclude(module: 'commons-codec')
+    }
+    runtime("openejb:openejb-loader:$openejbVersion") {
+        exclude(module: 'log4j')
+        exclude(module: 'openejb-core')
+        exclude(module: 'geronimo-jta_1.0.1B_spec')
+        exclude(module: 'geronimo-servlet_2.4_spec')
+        exclude(module: 'geronimo-ejb_2.1_spec')
+        exclude(module: 'geronimo-j2ee-connector_1.5_spec')
+    }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..0277b23
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,5 @@
+groovyVersion=3.0.0-SNAPSHOT
+luceneVersion=7.4.0
+eclipseOsgiVersion=3.10.0-v20140606-1445
+commonsHttpClientVersion=3.1
+openejbVersion=1.0
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..758de96
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a95009c
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/groovy/astbuilder/Main.groovy b/src/main/groovy/astbuilder/Main.groovy
new file mode 100644
index 0000000..03ffa46
--- /dev/null
+++ b/src/main/groovy/astbuilder/Main.groovy
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package astbuilder
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+import java.lang.annotation.ElementType
+import java.lang.annotation.Target
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Retention
+
+/**
+ * Marker interface to mark a method as something that should be invokable
+ * as a main() method. An AST transformation will later wire this together. 
+ *
+ * @author Hamlet D'Arcy
+ */
+
+@Retention (RetentionPolicy.SOURCE)
+@Target ([ElementType.METHOD])
+@GroovyASTTransformationClass (["examples.astbuilder.MainTransformation"])
+public @interface Main { 
+}
diff --git a/src/main/groovy/astbuilder/MainExample.groovy b/src/main/groovy/astbuilder/MainExample.groovy
new file mode 100644
index 0000000..2469a2c
--- /dev/null
+++ b/src/main/groovy/astbuilder/MainExample.groovy
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package astbuilder
+
+/**
+ * Exists to test the @Main annotation. At compile time, there will be a main()
+ * method added to this class that has the same body as the greet() method. 
+ * It can be invoked either by using either Java or Groovy to run the class. 
+ *
+ * @author Hamlet D'Arcy
+ */
+class MainExample {
+
+    @Main
+    public void greet() {
+        println "Hello from the greet() method!"
+    }
+}
diff --git a/src/main/groovy/astbuilder/MainIntegrationTest.groovy b/src/main/groovy/astbuilder/MainIntegrationTest.groovy
new file mode 100644
index 0000000..92052e4
--- /dev/null
+++ b/src/main/groovy/astbuilder/MainIntegrationTest.groovy
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package astbuilder
+
+import org.codehaus.groovy.tools.ast.TransformTestHelper
+import org.codehaus.groovy.control.CompilePhase
+
+/**
+ *
+ * This TestCase shows how to invoke an AST Transformation from a unit test.
+ * An IDE will let you step through the AST Transformation using this approach. 
+ *
+ * @author Hamlet D'Arcy
+ */
+
+class MainIntegrationTest extends GroovyTestCase {
+
+     public void testInvokeUnitTest() {
+        def invoker = new TransformTestHelper(new MainTransformation(), CompilePhase.CANONICALIZATION)
+
+        def file = new File('./MainExample.groovy')
+        assert file.exists()
+
+        def clazz = invoker.parse(file)
+        def tester = clazz.newInstance()
+        tester.main(null)       // main method added with AST transform
+    }
+}
diff --git a/src/main/groovy/astbuilder/MainTransformation.groovy b/src/main/groovy/astbuilder/MainTransformation.groovy
new file mode 100644
index 0000000..ce07916
--- /dev/null
+++ b/src/main/groovy/astbuilder/MainTransformation.groovy
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package astbuilder
+
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.transform.GroovyASTTransformation
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.ast.builder.AstBuilder
+
+/**
+ * If there is a method in a class with the @Main annotation on it, then this 
+ * transformation adds a real main(String[]) method to the class with the same
+ * method body as the annotated class. 
+ *
+ * @author Hamlet D'Arcy
+ */
+@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
+public class MainTransformation implements ASTTransformation {
+
+    // normally defined in org.objectweb.asm.Opcodes, but there duplicated
+    // here to make the build script simpler. 
+    static int PUBLIC = 1
+    static int STATIC = 8
+    
+    void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
+
+        // use guard clauses as a form of defensive programming. 
+        if (!astNodes) return 
+        if (!astNodes[0]) return 
+        if (!astNodes[1]) return 
+        if (!(astNodes[0] instanceof AnnotationNode)) return
+        if (astNodes[0].classNode?.name != Main.class.name) return
+        if (!(astNodes[1] instanceof MethodNode)) return 
+
+        MethodNode annotatedMethod = astNodes[1]
+        ClassNode declaringClass = annotatedMethod.declaringClass
+        MethodNode mainMethod = makeMainMethod(annotatedMethod)
+        declaringClass.addMethod(mainMethod)
+    }
+
+    /**
+    * Uses the AstBuilder to synthesize a main method, and then sets the body of
+    * the method to that of the source method. Notice how Void.TYPE is used as
+    * a return value instead of Void.class. This is required so that resulting method
+    * is void and not Void. 
+    */ 
+    MethodNode makeMainMethod(MethodNode source) {
+        def className = source.declaringClass.name
+        def methodName = source.name
+
+        def ast = new AstBuilder().buildFromString(CompilePhase.INSTRUCTION_SELECTION, false, """
+            package $source.declaringClass.packageName
+            
+            class $source.declaringClass.nameWithoutPackage {
+                public static void main(String[] args) {
+                    new $className().$methodName()
+                }
+            }
+        """)
+        ast[1].methods.find { it.name == 'main' }
+    }
+}
diff --git a/src/main/groovy/astbuilder/build.xml b/src/main/groovy/astbuilder/build.xml
new file mode 100644
index 0000000..3f31815
--- /dev/null
+++ b/src/main/groovy/astbuilder/build.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<project name="groovy-astbuilder-usage-example" default="compile-transform">
+
+    <!-- necessary groovy jars are assumed to be on your classpath. -->
+    <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" />
+    
+    <target name="init" description="cleanup old class files">
+        <delete dir="examples"/>
+    </target>
+
+    <target name="compile-transform" depends="init" description="Compiles the AST Transformation">
+    
+        <groovyc destdir="."
+                srcdir="."
+                includes="Main.groovy,MainTransformation.groovy" 
+                listfiles="true">
+        </groovyc>
+        
+        <echo>You can now run "groovy MainExample.groovy", "groovyc MainExample.groovy", or "groovy MainIntegrationTest.groovy" to see that the transformation worked.</echo>
+    </target>
+
+</project>
+
diff --git a/src/main/groovy/astbuilder/readme.txt b/src/main/groovy/astbuilder/readme.txt
new file mode 100644
index 0000000..25f7f99
--- /dev/null
+++ b/src/main/groovy/astbuilder/readme.txt
@@ -0,0 +1,42 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+AstBuilder and AST Transformation Example
+
+This example shows how to use the AstBuilder to add a public static void main(String[]) 
+method to a class. 
+
+The example requires ant in your path and the Groovy 1.7 (or greater) 
+Jar in your classpath. 
+
+To build the example run "ant" from the current directory. The default 
+target will compile the classes needed. The last step of the build 
+script prints out the command needed to run the example. 
+
+To run the example perform either of the following from the command lines: 
+  groovy MainExample.groovy
+  groovyc MainExample.groovy (and then invoke with java or view with javap)
+  
+The example should print: 
+  Hello from the greet() method!
+
+No exceptions should occur. 
+
+The MainIntegrationTest.groovy file shows how to invoke an ASTTransformation
+from a unit test. An IDE should be able to debug this. 
diff --git a/src/main/groovy/commandLineTools/AntMap.groovy b/src/main/groovy/commandLineTools/AntMap.groovy
new file mode 100644
index 0000000..741c817
--- /dev/null
+++ b/src/main/groovy/commandLineTools/AntMap.groovy
@@ -0,0 +1,84 @@
+#!/bin/env groovy
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+//
+// convert an ant build file into a format suitable for http://sf.net/projects/freemind
+//
+// by Jeremy Rayner - 2 Dec 2004
+// inspired by Sam Newman ( http://www.magpiebrain.com/archives/2004/12/02/antgui )
+//
+// usage:   groovy AntMap > build.mm
+
+buildFileName = "build.xml"  // default
+
+// handle command line params
+if (args.length > 0) {
+    buildFileName = args[0]
+}
+
+// header
+println "<map version='0.7.1'>"
+project = new XmlParser().parse(buildFileName)
+name = project['@name']
+println "<node TEXT='${name}'>"
+level = 0
+
+printChildren(project,level)
+
+
+
+def void printChildren(node,level) {
+    level++
+    node.each {
+        name = huntForName(it)
+        if (name != null) {
+            if (level > 1) {
+                println "<node TEXT='${name}' POSITION='right'>"
+            } else if (it.name() == 'property' || it.name() == 'path' ) {
+                if (it.children().size() > 0) {
+                    println "<node TEXT='${name}' POSITION='left' FOLDED='true'>"
+                } else {
+                    println "<node TEXT='${name}' POSITION='left'>"
+                }
+            } else if (it.children().size() > 0) {
+                println "<node TEXT='${name}' POSITION='right' FOLDED='true'>"
+            } else {
+                println "<node TEXT='${name}' POSITION='right'>"
+            }
+        }
+        if (it.children().size() > 0) printChildren(it,level)
+        if (name!=null)    println "</node>"
+    }
+}
+
+// footer
+println "</node></map>"
+
+
+def String huntForName(node) {
+    preferNodeNames = ["junitreport"]
+    if (node == null) return null
+    if (preferNodeNames.contains(node.name())) return node.name()
+    if (node['@name'] != null) return node['@name']
+    if (node['@todir'] != null) return node['@todir']
+    if (node['@dir'] != null) return node['@dir']
+    if (node['@refid'] != null) return node['@refid']
+    return node.name()
+}
diff --git a/src/main/groovy/commandLineTools/BigTests.groovy b/src/main/groovy/commandLineTools/BigTests.groovy
new file mode 100644
index 0000000..85fa13b
--- /dev/null
+++ b/src/main/groovy/commandLineTools/BigTests.groovy
@@ -0,0 +1,51 @@
+#!/bin/env groovy
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+//
+// output tests in a junit xml report that took greater than specified time
+//
+// by Jeremy Rayner - 15 Dec 2004
+//
+// usage:   groovy BigTests.groovy <TEST.xml> <time in secs>
+
+fileName = ""  // default
+timeCutOff = new Float("1.0")
+
+if (args.length > 1) {
+    fileName = args[0]
+    timeCutOff = new Float(args[1])
+} else {
+    println "usage: groovy BigTests.groovy <TEST.xml> <time in secs>"
+}
+
+testSuite = new XmlParser().parse(fileName)
+name = testSuite['@name']
+println "TestSuite: ${name}"
+bigTests = [:]
+testSuite.each {
+    if ("testcase" == it.name()) {
+        classname = it['@classname']
+        name = it['@name']
+        time = new Float(it['@time'])
+        if (time > timeCutOff) {
+            println "  ${time} - ${classname}.${name}()"
+        }
+    }
+}
+
diff --git a/src/main/groovy/commandLineTools/ListFiles.groovy b/src/main/groovy/commandLineTools/ListFiles.groovy
new file mode 100644
index 0000000..e3d1f71
--- /dev/null
+++ b/src/main/groovy/commandLineTools/ListFiles.groovy
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+public class ListFilesManualTest { 
+
+    static void main(args) {
+        println("Called main with ${args}")
+        listFiles(Arrays.asList(args))
+    }
+    
+    static String getPath(file) {
+        return file.absolutePath
+    }
+
+    static void listFiles(dirs) {
+        println("called with ${dirs}")
+        
+        for(dir in dirs) {
+            println("dir: ${dir}")
+            
+            def files = new java.io.File(dir).listFiles()
+
+            for (f in files) {
+                println(getPath(f))
+            } 
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/commandLineTools/Reflections.groovy b/src/main/groovy/commandLineTools/Reflections.groovy
new file mode 100644
index 0000000..4be7551
--- /dev/null
+++ b/src/main/groovy/commandLineTools/Reflections.groovy
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+ * Echoes back whatever is thrown at it (with a <br> at end for browsers) ...
+ * @author <a href="mailto:jeremy.rayner@gmail.com">Jeremy Rayner</a>
+ * 
+ * invoke using
+ *    groovy -l 80 Reflections.groovy
+ * 
+ *       (where 80 is the port to listen for requests upon)
+ */
+
+// echo, echo, echo...
+println "${line} <br>"
+
+//assume no input means we've finished...
+if (line == "") {
+    // clean up gracefully, closing sockets etc
+    return "success"
+}
diff --git a/src/main/groovy/commandLineTools/SimpleWebServer.groovy b/src/main/groovy/commandLineTools/SimpleWebServer.groovy
new file mode 100644
index 0000000..09835d7
--- /dev/null
+++ b/src/main/groovy/commandLineTools/SimpleWebServer.groovy
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+ * Simple web server
+ * @author <a href="mailto:jeremy.rayner@gmail.com">Jeremy Rayner</a>
+ * 
+ * invoke using
+ *    groovy -l 80 SimpleWebServer.groovy
+ * 
+ *       (where 80 is the port to listen for requests upon)
+ */
+if (init) {
+    headers = [:] 
+    binaryTypes = ["gif","jpg","png"]          
+    mimeTypes = [
+        "css" : "text/css",         
+        "gif" : "image/gif",
+        "htm" : "text/html",         
+        "html": "text/html",         
+        "jpg" : "image/jpeg",         
+        "png" : "image/png"
+    ]                                 
+}
+
+// parse the request
+if (line.toLowerCase().startsWith("get")) {
+    content = line.tokenize()[1]
+} else {
+    h = line.tokenize(":")
+    headers[h[0]] = h[1]
+}
+
+// all done, now process request
+if (line.size() == 0) {
+    processRequest()
+    return "success"
+}
+
+// ------------------------
+
+def processRequest() {
+    if (content.indexOf("..") < 0) { //simplistic security
+        // simple file browser rooted from current dir
+        f = new File("." + content)
+        if (f.isDirectory()) {
+            printDirectoryListing(f)
+        } else {
+            extension = content.substring(content.lastIndexOf(".") + 1)
+            printHeaders(mimeTypes.get(extension,"text/plain"))          
+                      
+            if (binaryTypes.contains(extension)) {
+                socket.outputStream.write(f.readBytes())
+            } else {
+                println(f.text)
+            }
+        }
+    }
+}
+
+def printDirectoryListing(f) {
+    printHeaders("text/html")          
+    println "<html><head></head><body>"
+    for (i in f.list().toList().sort()) {
+        if ("/" == content) { content = "" } // special case for root document
+        println "<a href='${content}/${i}'>${i}</a><br>"
+    }
+    println "</body></html>"
+}
+
+def printHeaders(mimeType) {
+    println "HTTP/1.0 200 OK"
+    println "Content-Type: ${mimeType}"
+    println ""          
+}
diff --git a/src/main/groovy/console/MortgageCalculator.groovy b/src/main/groovy/console/MortgageCalculator.groovy
new file mode 100644
index 0000000..375e244
--- /dev/null
+++ b/src/main/groovy/console/MortgageCalculator.groovy
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/** 
+ * Mortgage Calculator
+ * @author: Jeremy Rayner
+ * based on algorithms by Jeff Louie, Dr W Carlini and Newton
+ */
+
+println "__..::~~'''~~::..__"
+println "Mortgage Calculator"
+println "~~~~~~~~~~~~~~~~~~~"
+println "Please input 3 of the 4 values in your mortgage calculation"
+println "This program will then calculate the value you leave blank"
+println ""
+
+def variables = [
+    "Amount of mortgage" : 0.0, 
+    "Annual interest rate (%)" : 0.0, 
+    "Loan duration (months)" : 0.0, 
+    "Monthly payments" : 0.0
+]
+
+for (entry in variables.entrySet()) {
+    print("${entry.key}:")
+    def userInput = System.in.readLine()
+    if ("" == userInput) {
+        valueToCalculate = entry.key
+    } else {
+        entry.value = userInput.toDouble()
+    }
+}
+
+println "$valueToCalculate = ${calculateValueOf(valueToCalculate)}"
+
+
+
+
+
+def calculateValueOf(valueToCalculate) {
+    def result = 0
+    def principal = variables["Amount of mortgage"]
+    def interest = variables["Annual interest rate (%)"] / 1200
+    def months = variables["Loan duration (months)"]
+    def payment = variables["Monthly payments"]
+
+    switch (valueToCalculate) {
+    case "Amount of mortgage":
+        result = 1 + interest
+        result = 1/Math.pow(result,months)
+        result = ((1-result)/interest) * payment
+        break           
+    case "Loan duration (months)":
+        result = (1 - (principal * interest / payment))
+        result = Math.log(result)
+        result = - result / Math.log(1 + interest)  
+        break
+    case "Monthly payments":
+        result = 1 + interest
+        result = 1 / Math.pow(result,months)
+        result = (principal * interest) / (1 - result)
+        break          
+    case "Annual interest rate (%)":
+        result = payment / principal
+        def diff = 100; def accuracy = 0.00001; def maxIterations = 1000
+        def index = 0
+        while ((diff > accuracy) && (index < maxIterations)) {
+            def temp = result
+            def numerator = (principal * temp / payment) + Math.pow((1 + temp), -months) - 1
+            def denominator= (principal / payment) - months * Math.pow((1 + temp), (-months - 1))
+            result = temp - (numerator / denominator)
+            diff = result - temp
+            diff = Math.abs(diff)
+            index++
+        }
+        result *= 1200
+        break           
+    }
+    return result
+}
diff --git a/src/main/groovy/console/knowYourTables.groovy b/src/main/groovy/console/knowYourTables.groovy
new file mode 100644
index 0000000..1343365
--- /dev/null
+++ b/src/main/groovy/console/knowYourTables.groovy
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/** 
+ * Simple mathematics quiz
+ * @author: Jeremy Rayner
+ * based on algorithms from INPUT/Marshall Cavendish/1984
+ */
+while (true) {
+    try {
+        def n = (int)(Math.random() * 12) + 1
+        println "What is $n times 9?"
+        def a = System.in.readLine().toInteger()
+        if (a == n * 9) println "Correct"
+              
+    } catch (Exception e) {
+        println "The computer didn't understand your input"
+    }
+}
diff --git a/src/main/groovy/console/thinkOfANumber.groovy b/src/main/groovy/console/thinkOfANumber.groovy
new file mode 100644
index 0000000..7a42cf9
--- /dev/null
+++ b/src/main/groovy/console/thinkOfANumber.groovy
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/** 
+ * Simple game
+ * @author: Jeremy Rayner
+ * based on algorithms from INPUT/Marshall Cavendish/1984
+ */
+while (true) {
+    try {
+        int x = Math.random() * 6
+        print "The computer has chosen a number between 0 and 5. Can you guess it?"
+              
+        def line = System.in.readLine()
+        int g = line.toInteger()
+        if (g == x) {
+           println "Well done" 
+        } else {
+           println "Tough luck - you're wrong"
+        }
+     } catch (NumberFormatException e) {
+         println "The computer didn't understand '$line'"
+     }
+}
diff --git a/src/main/groovy/groovy/j2ee/CreateData.groovy b/src/main/groovy/groovy/j2ee/CreateData.groovy
new file mode 100644
index 0000000..a0bef2a
--- /dev/null
+++ b/src/main/groovy/groovy/j2ee/CreateData.groovy
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+println("Called with context: " + context)
+
+db = context.lookup("/client/tools/DatabaseHome").create()
+
+println("About to do queries on: " + db)
+
+queries = [
+        "CREATE TABLE account ( ssn CHAR(11) PRIMARY KEY, first_name CHAR(20), last_name CHAR(20), balance INT)",
+        "CREATE TABLE entity ( id INT PRIMARY KEY AUTO INCREMENT, first_name CHAR(20), last_name CHAR(20) )"
+]
+
+for (sql in queries) {
+    println("evaluating: " + sql)
+    db.execute(sql)
+}
+
+println("creating entity bean")
+
+context.lookup("/client/tests/entity/bmp/BasicBmpHome").create("Groovy Dain")
+
+println("Done")
+
+"OK"
\ No newline at end of file
diff --git a/src/main/groovy/groovy/j2ee/J2eeConsole.java b/src/main/groovy/groovy/j2ee/J2eeConsole.java
new file mode 100644
index 0000000..538ebf6
--- /dev/null
+++ b/src/main/groovy/groovy/j2ee/J2eeConsole.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.j2ee;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.util.Properties;
+
+/**
+ * A J2EE console
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class J2eeConsole {
+
+    public static void main(String[] args) {
+        if (args.length <= 0) {
+            System.out.println("Usage: home [configuaration] [localcopy]");
+            return;
+        }
+
+        String home = args[0];
+
+        Properties p = new Properties();
+        System.setProperty("openejb.home", home);
+        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.openejb.client.LocalInitialContextFactory");
+        p.put("openejb.loader", "embed");
+        p.put("openejb.home", home);
+
+        if (args.length > 1) {
+            String conf = args[1];
+            System.setProperty("openejb.configuration", conf);
+            p.put("openejb.configuration", conf);
+        }
+        if (args.length > 2) {
+            String copy = args[2];
+            System.setProperty("openejb.localcopy", copy);
+            p.put("openejb.localcopy", copy);
+        }
+        try {
+            InitialContext ctx = new InitialContext(p);
+
+            GroovyShell shell = new GroovyShell();
+            shell.setVariable("context", ctx);
+            //shell.evaluate("src/test/groovy/j2ee/CreateData.groovy");
+
+            //shell.evaluate("src/main/groovy/ui/Console.groovy");
+            GroovyObject console = (GroovyObject) InvokerHelper.invokeConstructorOf("groovy.ui.Console", null);
+            console.setProperty("shell", shell);
+            console.invokeMethod("run", null);
+            /*
+            */
+        }
+        catch (Exception e) {
+            System.out.println("Caught: " + e);
+        }
+    }
+}
diff --git a/src/main/groovy/groovy/model/MvcDemo.groovy b/src/main/groovy/groovy/model/MvcDemo.groovy
new file mode 100644
index 0000000..dc31126
--- /dev/null
+++ b/src/main/groovy/groovy/model/MvcDemo.groovy
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.model
+
+import groovy.swing.SwingBuilder
+/**
+ * 
+ */
+class MvcDemo {
+    
+    def frame
+    def swing
+    
+    void run() {
+        swing = new SwingBuilder()
+        
+        def frame = swing.frame(title:'MVC Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            panel {
+                borderLayout()
+                scrollPane(constraints:CENTER) {
+                    table() {
+                        tableModel(list:[ ['name':'James', 'location':'London'], ['name':'Bob', 'location':'Atlanta'] ]) {
+                            propertyColumn(header:'Name', propertyName:'name')
+                            propertyColumn(header:'Location', propertyName:'location')
+                        }
+                    }
+                }
+            }
+        }        
+        frame.show()
+    }
+ 
+    void showAbout() {
+         def pane = swing.optionPane(message:'This demo shows how you can create UI models from simple MVC models')
+         def dialog = pane.createDialog(frame, 'About MVC Demo')
+         dialog.show()
+    }
+}
diff --git a/src/main/groovy/groovy/swing/Demo.java b/src/main/groovy/groovy/swing/Demo.java
new file mode 100644
index 0000000..db997ca
--- /dev/null
+++ b/src/main/groovy/groovy/swing/Demo.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyClassLoader;
+import groovy.util.GroovyTestCase;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class Demo extends GroovyTestCase {
+    ClassLoader parentLoader = getClass().getClassLoader();
+    protected GroovyClassLoader loader =
+        (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyClassLoader(parentLoader);
+            }
+        });
+
+    public static void main(String[] args) throws Exception {
+        Demo demo = new Demo();
+        GroovyObject object = demo.compile("src/examples/groovy/swing/SwingDemo.groovy");
+        object.invokeMethod("run", null);
+    }
+
+    protected GroovyObject compile(String fileName) throws Exception {
+        Class groovyClass = loader.parseClass(new GroovyCodeSource(new File(fileName)));
+        GroovyObject object = (GroovyObject) groovyClass.newInstance();
+        assertTrue(object != null);
+        return object;
+    }
+}
diff --git a/src/main/groovy/groovy/swing/MyTableModel.java b/src/main/groovy/groovy/swing/MyTableModel.java
new file mode 100644
index 0000000..fa28bf5
--- /dev/null
+++ b/src/main/groovy/groovy/swing/MyTableModel.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.table.AbstractTableModel;
+
+/** 
+ * A sample table model
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class MyTableModel extends AbstractTableModel {
+
+    private static final Logger log = Logger.getLogger(MyTableModel.class.getName());
+
+    public MyTableModel() {
+    }
+
+    final String[] columnNames = { "First Name", "Last Name", "Sport", "# of Years", "Vegetarian" };
+    final Object[][] data = { { "Mary", "Campione", "Snowboarding", Integer.valueOf(5), new Boolean(false)}, {
+            "Alison", "Huml", "Rowing", Integer.valueOf(3), new Boolean(true)
+            }, {
+            "Kathy", "Walrath", "Chasing toddlers", Integer.valueOf(2), new Boolean(false)
+            }, {
+            "Mark", "Andrews", "Speed reading", Integer.valueOf(20), new Boolean(true)
+            }, {
+            "Angela", "Lih", "Teaching high school", Integer.valueOf(4), new Boolean(false)
+            }
+    };
+
+    public int getColumnCount() {
+        return columnNames.length;
+    }
+
+    public int getRowCount() {
+        return data.length;
+    }
+
+    public String getColumnName(int col) {
+        return columnNames[col];
+    }
+
+    public Object getValueAt(int row, int col) {
+        return data[row][col];
+    }
+
+    /*
+     * JTable uses this method to determine the default renderer/
+     * editor for each cell.  If we didn't implement this method,
+     * then the last column would contain text ("true"/"false"),
+     * rather than a check box.
+     */
+    public Class getColumnClass(int c) {
+        return getValueAt(0, c).getClass();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's
+     * editable.
+     */
+    public boolean isCellEditable(int row, int col) {
+        //Note that the data/cell address is constant,
+        //no matter where the cell appears onscreen.
+        if (col < 2) {
+            return false;
+        }
+        else {
+            return true;
+        }
+    }
+
+    /*
+     * Don't need to implement this method unless your table's
+     * data can change.
+     */
+    public void setValueAt(Object value, int row, int col) {
+        if (log.isLoggable(Level.FINE)) {
+            log.fine(
+                "Setting value at " + row + "," + col + " to " + value + " (an instance of " + value.getClass() + ")");
+        }
+
+        if (data[0][col] instanceof Integer && !(value instanceof Integer)) {
+            //With JFC/Swing 1.1 and JDK 1.2, we need to create    
+            //an Integer from the value; otherwise, the column     
+            //switches to contain Strings.  Starting with v 1.3,   
+            //the table automatically converts value to an Integer,
+            //so you only need the code in the 'else' part of this 
+            //'if' block.                                          
+            //XXX: See TableEditDemo.java for a better solution!!!
+            try {
+                data[row][col] = Integer.valueOf(value.toString());
+                fireTableCellUpdated(row, col);
+            }
+            catch (NumberFormatException e) {
+                log.log(Level.SEVERE, "The \"" + getColumnName(col) + "\" column accepts only integer values.");
+            }
+        }
+        else {
+            data[row][col] = value;
+            fireTableCellUpdated(row, col);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/groovy/groovy/swing/SwingDemo.groovy b/src/main/groovy/groovy/swing/SwingDemo.groovy
new file mode 100644
index 0000000..6158032
--- /dev/null
+++ b/src/main/groovy/groovy/swing/SwingDemo.groovy
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+import groovy.model.MvcDemo
+import groovy.swing.SwingBuilder
+
+class SwingDemo {
+
+    def swing = new SwingBuilder()
+    def frame
+    
+    static void main(args) {
+        def demo = new SwingDemo()
+        demo.run()
+    }
+
+    void run() {
+        def frame = swing.frame(
+            title:'This is a Frame',
+            location:[100,100],
+            size:[800,400],
+            defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE) {
+
+            menuBar {
+                menu(text:'File') {
+                    menuItem() {
+                        action(name:'New', closure:{ println("clicked on the new menu item!") })
+                    }
+                    menuItem() {
+                        action(name:'Open', closure:{ println("clicked on the open menu item!") })
+                    }
+                    separator()
+                    menuItem() {
+                        action(name:'Save', enabled:false, closure:{ println("clicked on the Save menu item!") })
+                    }
+                }
+                menu(text:'Demos') {
+                    menuItem() {
+                        action(name:'Simple TableModel Demo', closure:{ showGroovyTableDemo() })
+                    }
+                    menuItem() {
+                        action(name:'MVC Demo', closure:{ showMVCDemo() })
+                    }
+                    menuItem() {
+                        action(name:'TableLayout Demo', closure:{ showTableLayoutDemo() })
+                    }
+                }
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            splitPane {
+                panel(border:BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder(), 'titled border')) {
+                    borderLayout()
+                    vbox(constraints:NORTH) {
+                        panel {
+                            borderLayout()
+                            label(text:'Name', constraints:WEST, toolTipText:'This is the name field')
+                            textField(text:'James', constraints:CENTER, toolTipText:'Enter the name into this field')
+                        }
+                        panel {
+                            borderLayout()
+                            label(text:'Location', constraints:WEST, toolTipText:'This is the location field')
+                            comboBox(items:['Atlanta', 'London', 'New York'], constraints:CENTER, toolTipText:'Choose the location into this field')
+                        }
+                        button(text:'Click Me', actionPerformed:{event -> println("closure fired with event: " + event) })
+                    }
+                    scrollPane(constraints:CENTER, border:BorderFactory.createRaisedBevelBorder()) {
+                        textArea(text:'Some text goes here', toolTipText:'This is a large text area to type in text')
+                    }
+                }
+                scrollPane {
+                    table(model:new MyTableModel())
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+        // this version doesn't auto-size & position the dialog
+        /*
+        def dialog = swing.dialog(owner:frame, title:'About GroovySwing') {
+            optionPane(message:'Welcome to the wonderful world of GroovySwing')
+        }
+        */
+         def pane = swing.optionPane(message:'Welcome to the wonderful world of GroovySwing')
+         def dialog = pane.createDialog(frame, 'About GroovySwing')
+         dialog.show()
+    }
+    
+    void showGroovyTableDemo() {
+        def demo = new TableDemo()
+        demo.run()
+    }
+
+    void showMVCDemo() {
+        def demo = new MvcDemo()
+        demo.run()
+    }
+
+    void showTableLayoutDemo() {
+        def demo = new TableLayoutDemo()
+        demo.run()
+    }
+}
diff --git a/src/main/groovy/groovy/swing/TableDemo.groovy b/src/main/groovy/groovy/swing/TableDemo.groovy
new file mode 100644
index 0000000..abc2659
--- /dev/null
+++ b/src/main/groovy/groovy/swing/TableDemo.groovy
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+import groovy.swing.SwingBuilder
+
+/**
+ * Demonstrates the use of the Groovy TableModels for viewing tables of any List of objects
+ */
+class TableDemo {
+    
+    // properties
+    def frame
+    def swing
+    
+    static void main(args) {
+        def demo = new TableDemo()
+        demo.run()
+    }
+    
+    void run() {
+        swing = new SwingBuilder()
+        
+        frame = swing.frame(title:'Groovy TableModel Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            panel {
+                borderLayout()
+                scrollPane(constraints:CENTER) {
+                    table() {
+                        def model = [['name':'James', 'location':'London'], ['name':'Bob', 'location':'Atlanta'], ['name':'Geir', 'location':'New York']]
+
+                        tableModel(list:model) {
+                            closureColumn(header:'Name', read:{row -> return row.name})
+                            closureColumn(header:'Location', read:{row -> return row.location})
+                        }
+                    }
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+         def pane = swing.optionPane(message:'This demo shows how GroovySwing can use Groovy closures to create simple table models')
+         def dialog = pane.createDialog(frame, 'About GroovySwing')
+         dialog.show()
+    }
+}
diff --git a/src/main/groovy/groovy/swing/TableLayoutDemo.groovy b/src/main/groovy/groovy/swing/TableLayoutDemo.groovy
new file mode 100644
index 0000000..df2c223
--- /dev/null
+++ b/src/main/groovy/groovy/swing/TableLayoutDemo.groovy
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+import groovy.swing.SwingBuilder
+
+/**
+ * Demonstrates the use of the table layout
+ */
+class TableLayoutDemo {
+    
+    def frame
+    def swing
+    
+    void run() {
+        swing = new SwingBuilder()
+        
+        frame = swing.frame(title:'TableLayout Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            tableLayout {
+                tr {
+                    td {
+                        label(text:'name')
+                    }
+                    td(colfill:true) {
+                        textField(text:'James')
+                    }
+                }
+                tr {
+                    td {
+                        label(text:'location')
+                    }
+                    td(colfill:true) {
+                        textField(text:'London')
+                    }
+                }
+                tr {
+                    td(colspan:2, align:'center') {
+                        button(text:'OK')
+                    }
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+         def pane = swing.optionPane(message:'This demo shows how you can use HTML style table layouts with Swing components')
+         def dialog = pane.createDialog(frame, 'About TableLayout Demo')
+         dialog.show()
+    }
+}
diff --git a/src/main/groovy/groovy2d/paintingByNumbers.groovy b/src/main/groovy/groovy2d/paintingByNumbers.groovy
new file mode 100644
index 0000000..5a69baf
--- /dev/null
+++ b/src/main/groovy/groovy2d/paintingByNumbers.groovy
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/** 
+ * Simple patchwork graphics demo
+ * @author: Jeremy Rayner, changes by Dierk Koenig
+ */
+import javax.swing.WindowConstants as WC
+import groovy.swing.SwingBuilder
+
+def width = 500, height = 400, blockSize = 10
+def g = createGraphics(width, height)
+
+// main loop
+while (true) {
+    drawBlock(width, height, blockSize, g)
+}
+
+// random integer
+def rnd(upperBound){
+    (int)(Math.random() * upperBound)
+}
+
+// draw a random coloured square within bounds
+def drawBlock(w, h, b, g) {
+    def row    = rnd(h / b)
+    def column = rnd(w / b)
+    def colour = new java.awt.Color(rnd(255),rnd(255),rnd(255))
+    g.color = colour
+    g.fillRect(column * b, row * b, b, b)
+}
+
+// create a new frame and clear screen
+def createGraphics(w, h) {
+    def frame = new SwingBuilder().frame(
+        title:'Painting by numbers',
+        location:[20,20],
+        size:[w, h],
+        defaultCloseOperation:WC.EXIT_ON_CLOSE
+    )
+    frame.show()
+              
+    // obtain graphics context
+    def gfx = frame.graphics
+              
+    // clear screen
+    gfx.color = java.awt.Color.BLACK
+    gfx.fillRect(0, 0, w, h)
+
+    return gfx
+}
diff --git a/src/main/groovy/groovyShell/ArithmeticShell.groovy b/src/main/groovy/groovyShell/ArithmeticShell.groovy
new file mode 100644
index 0000000..d1a5659
--- /dev/null
+++ b/src/main/groovy/groovyShell/ArithmeticShell.groovy
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.MultipleCompilationErrorsException
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.control.customizers.SecureASTCustomizer
+import org.codehaus.groovy.control.messages.ExceptionMessage
+import static org.codehaus.groovy.syntax.Types.*
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.expr.BinaryExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.PropertyExpression
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression
+import org.codehaus.groovy.ast.expr.PrefixExpression
+import org.codehaus.groovy.ast.expr.PostfixExpression
+import org.codehaus.groovy.ast.expr.TernaryExpression
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression
+import org.codehaus.groovy.ast.expr.BooleanExpression
+import org.codehaus.groovy.ast.expr.ClassExpression
+
+/**
+ * The arithmetic shell is similar to a GroovyShell in that it can evaluate text as
+ * code and return a result. It is not a subclass of GroovyShell because it does not widen
+ * the contract of GroovyShell, instead it narrows it. Using one of these shells like a
+ * GroovyShell would result in many runtime errors.
+ *
+ * @author Hamlet D'Arcy (hamletdrc@gmail.com)
+ * @author Guillaume Laforge
+ */
+class ArithmeticShell {
+
+    /**
+     * Compiles the text into a Groovy object and then executes it, returning the result.
+     * @param text
+     *       the script to evaluate typed as a string
+     * @throws SecurityException
+     *       most likely the script is doing something other than arithmetic
+     * @throws IllegalStateException
+     *       if the script returns something other than a number
+     */
+    Number evaluate(String text) {
+        try {
+            final ImportCustomizer imports = new ImportCustomizer().addStaticStars('java.lang.Math') // add static import of java.lang.Math
+            final SecureASTCustomizer secure = new SecureASTCustomizer()
+            secure.with {
+                closuresAllowed = false
+                methodDefinitionAllowed = false
+                
+                importsWhitelist = []
+                staticImportsWhitelist = []
+                staticStarImportsWhitelist = ['java.lang.Math'] // only java.lang.Math is allowed
+
+                tokensWhitelist = [
+                        PLUS,
+                        MINUS,
+                        MULTIPLY,
+                        DIVIDE,
+                        MOD,
+                        POWER,
+                        PLUS_PLUS,
+                        MINUS_MINUS,
+                        COMPARE_EQUAL,
+                        COMPARE_NOT_EQUAL,
+                        COMPARE_LESS_THAN,
+                        COMPARE_LESS_THAN_EQUAL,
+                        COMPARE_GREATER_THAN,
+                        COMPARE_GREATER_THAN_EQUAL,
+                ].asImmutable()
+
+                constantTypesClassesWhiteList = [
+                        Integer,
+                        Float,
+                        Long,
+                        Double,
+                        BigDecimal,
+                        Integer.TYPE,
+                        Long.TYPE,
+                        Float.TYPE,
+                        Double.TYPE
+                ].asImmutable()
+
+                receiversClassesWhiteList = [
+                        Math,
+                        Integer,
+                        Float,
+                        Double,
+                        Long,
+                        BigDecimal
+                ].asImmutable()
+
+                statementsWhitelist = [
+                        BlockStatement,
+                        ExpressionStatement
+                ].asImmutable()
+
+                expressionsWhitelist = [
+                        BinaryExpression,
+                        ConstantExpression,
+                        MethodCallExpression,
+                        StaticMethodCallExpression,
+                        ArgumentListExpression,
+                        PropertyExpression,
+                        UnaryMinusExpression,
+                        UnaryPlusExpression,
+                        PrefixExpression,
+                        PostfixExpression,
+                        TernaryExpression,
+                        ElvisOperatorExpression,
+                        BooleanExpression,
+                        // ClassExpression needed for processing of MethodCallExpression, PropertyExpression
+                        // and StaticMethodCallExpression
+                  ClassExpression
+                ].asImmutable()
+
+            }
+            CompilerConfiguration config = new CompilerConfiguration()
+            config.addCompilationCustomizers(imports, secure)
+            GroovyClassLoader loader = new GroovyClassLoader(this.class.classLoader, config)
+            Class clazz = loader.parseClass(text)
+            Script script = (Script) clazz.newInstance();
+            Object result = script.run()
+            if (!(result instanceof Number)) throw new IllegalStateException("Script returned a non-number: $result");
+            return (Number) result
+        } catch (SecurityException ex) {
+            throw new SecurityException("Could not evaluate script: $text", ex)
+        } catch (MultipleCompilationErrorsException mce) {
+            //this allows compilation errors to be seen by the user       
+            mce.errorCollector.errors.each {
+                if (it instanceof ExceptionMessage && it.cause instanceof SecurityException) {
+                    throw it.cause
+                }
+            }
+            throw mce
+        }
+    }
+}
diff --git a/src/main/groovy/groovyShell/ArithmeticShellTest.groovy b/src/main/groovy/groovyShell/ArithmeticShellTest.groovy
new file mode 100644
index 0000000..2c9e982
--- /dev/null
+++ b/src/main/groovy/groovyShell/ArithmeticShellTest.groovy
@@ -0,0 +1,108 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+* Unit test for ArithmeticShell.
+* Requires JUnit to be in path, just like any other GroovyTestCase. 
+*
+* @author Hamlet D'Arcy
+*/
+class ArithmeticShellTest extends GroovyTestCase {
+
+    private ArithmeticShell shell
+
+    void setUp() {
+        shell = new ArithmeticShell()
+    }
+
+    Number evaluate(String text) {
+        shell.evaluate(text)
+    }
+
+    void testEvaluate_SuccessfulPaths() {
+        assert 2.9073548971824276E135 == evaluate("((6L / 2f) - 1) ** 4.5e2")
+        assert -6.816387600233341 == evaluate("10 * Math.sin(15/-20)")
+        assert 1.0 == evaluate("Math.cos(2*Math.PI)")
+        assert 74.17310622494026 == shell.evaluate("80*Math.E**(-(+(11++/40)**2))")
+        assert 2147483646 == evaluate("Integer.MAX_VALUE - ++2%2")
+        assert 6 == evaluate("++(5)")
+        assert 0 == evaluate("5 < 4 ? 1 : 0")
+        assert 0 == evaluate("5 != 4 ? 0 : 1")
+        assert 0 == evaluate("5 < 4 ?: 0 ")
+    }
+
+    void testEvaluate_StaticImportOfMath() {
+        assert 6.283185307179586 == evaluate("2*PI")
+        assert 0.5403023058681398 == evaluate("cos(1)")
+        assert 1.0 == evaluate("cos(2*PI)")
+    }
+
+    void testEvaluate_Failures() {
+        shouldFail(SecurityException) {
+            evaluate("Double.valueOf(\"5\")")
+        }
+
+        shouldFail(SecurityException) {
+            evaluate("import java.text.DateFormat; 5")
+        }
+
+        shouldFail(SecurityException) {
+            evaluate("import static java.lang.System.*; 6 * out")
+        }
+
+        shouldFail(SecurityException) {
+            evaluate("def x = 5+3;x.toString()")
+        }
+
+        shouldFail(SecurityException) {
+            evaluate("new File();Double.valueOf('5')")
+        }
+
+        shouldFail(SecurityException) {
+             // without statement whitelist set, this causes the arithmetic shell to
+             // enter an infinite loop
+             evaluate("while (1);")    
+        }
+
+        shouldFail(SecurityException) {
+             // without statement whitelist set, security exception is still thrown as it should,
+             // but with the error message that closures are not allowed, which may be confusing
+             evaluate("for (;;);")
+        }
+
+        shouldFail(SecurityException) {
+             // without statement whitelist set, no exception is thrown
+             evaluate("if (1) 12 else 15")    
+        }
+
+        shouldFail(SecurityException) {
+             // without expression whitelist set, no exception is thrown
+             evaluate("[1,2]; 1")
+        }
+
+        shouldFail(SecurityException) {
+             // without expression whitelist set, no exception is thrown
+             evaluate("[1:2]; 1")
+        }
+
+        shouldFail(SecurityException) {
+             // without expression whitelist set, no exception is thrown
+             evaluate("new Object(); 1")
+        }
+    }
+}
diff --git a/src/main/groovy/groovyShell/BlacklistingShell.groovy b/src/main/groovy/groovyShell/BlacklistingShell.groovy
new file mode 100644
index 0000000..becff49
--- /dev/null
+++ b/src/main/groovy/groovyShell/BlacklistingShell.groovy
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.MultipleCompilationErrorsException
+import org.codehaus.groovy.control.customizers.SecureASTCustomizer
+import org.codehaus.groovy.control.messages.ExceptionMessage
+import org.codehaus.groovy.ast.expr.MethodPointerExpression
+
+
+/**
+ * The blacklisting shell is similar to a GroovyShell in that it can evaluate text as
+ * code and return a result. It is intended as an example of using blacklisting to prevent
+ * running methods on a class - in this case, java.lang.System.  Please note that in creating
+ * any secure environment, there is no substitution for using a SecurityManager.
+ *
+ * Amoung the many different calls this class prevents are:
+ *   System.exit(0)
+ *   Eval.me("System.exit(0)")
+ *   evaluate("System.exit(0)")
+ *   (new GroovyShell()).evaluate("System.exit(0)")
+ *   Class.forName("java.lang.System").exit(0)
+ *   System.&exit.call(0)
+ *   System.getMetaClass().invokeMethod("exit",0)
+ *   def s = System; s.exit(0)
+ *   Script t = this; t.evaluate("System.exit(0)")
+ *
+ * The restrictions required, however, also prevent the following code from working:
+ *   println "test"
+ *   def s = "test" ; s.count("t") 
+ *
+ * @author Jim Driscoll (jamesgdriscoll@gmail.com)
+ */
+class BlacklistingShell {
+    
+    /**
+     * Compiles the text into a Groovy object and then executes it, returning the result.
+     * Prevents calling any method on java.lang.System within the VM
+     * @param text
+     *       the script to evaluate typed as a string
+     * @throws SecurityException
+     *       most likely the script is doing something other than arithmetic
+     * @throws IllegalStateException
+     *       if the script returns something other than a number
+     */
+    def evaluate(String text) {
+        try {
+            final SecureASTCustomizer secure = new SecureASTCustomizer()
+            secure.with {
+
+                receiversClassesBlackList = [
+                    Object,
+                    Script,
+                    GroovyShell,
+                    Eval,
+                    System,
+                ].asImmutable()
+                
+                expressionsBlacklist = [MethodPointerExpression].asImmutable()
+                
+            }
+            CompilerConfiguration config = new CompilerConfiguration()
+            config.addCompilationCustomizers(secure)
+            GroovyClassLoader loader = new GroovyClassLoader(this.class.classLoader, config)
+            Class clazz = loader.parseClass(text)
+            Script script = (Script) clazz.newInstance();
+            Object result = script.run()
+            return result
+        } catch (SecurityException ex) {
+            throw new SecurityException("Could not evaluate script: $text", ex)
+        } catch (MultipleCompilationErrorsException mce) {
+            //this allows compilation errors to be seen by the user       
+            mce.errorCollector.errors.each {
+                if (it instanceof ExceptionMessage && it.cause instanceof SecurityException) {
+                    throw it.cause
+                }
+            }
+            throw mce
+        }
+    }            
+}
\ No newline at end of file
diff --git a/src/main/groovy/groovyShell/BlacklistingShellTest.groovy b/src/main/groovy/groovyShell/BlacklistingShellTest.groovy
new file mode 100644
index 0000000..e24e149
--- /dev/null
+++ b/src/main/groovy/groovyShell/BlacklistingShellTest.groovy
@@ -0,0 +1,82 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+* Unit test for BlacklistingShell.
+* Requires JUnit to be in path, just like any other GroovyTestCase. 
+*
+* @author Jim Driscoll jamesgdriscoll@gmail.com
+*/
+class BlacklistingShellTest extends GroovyTestCase {
+
+    private BlacklistingShell shell
+
+    void setUp() {
+        shell = new BlacklistingShell()
+    }
+
+    Object evaluate(String text) {
+        shell.evaluate(text)
+    }
+
+    void testEvaluate_SuccessfulPaths() {
+        assert 6 == evaluate("++(5)")
+        assert 0 == evaluate("5 < 4 ? 1 : 0")
+        assert 0 == evaluate("5 != 4 ? 0 : 1")
+        assert 0 == evaluate("5 < 4 ?: 0 ")
+    }
+
+    void testEvaluate_Failures() {
+        shouldFail(SecurityException) {
+            evaluate('def c = System.class; c.forName("java.lang.System").exit(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('Class.forName("java.lang.System").exit(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('System.exit(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('def e = System.&exit; e.call(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('System.&exit.call(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('System.getMetaClass().invokeMethod("exit",0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('evaluate("System.exit(0)")')
+        }
+        shouldFail(SecurityException) {
+            evaluate('(new GroovyShell()).evaluate("System.exit(0)")')
+        }
+        shouldFail(SecurityException) {
+            evaluate('def sh = new GroovyShell(); sh.evaluate("System.exit(0)")')
+        }
+        shouldFail(SecurityException) {
+            evaluate('Eval.me("System.exit(0)")')
+        }
+        shouldFail(SecurityException) {
+            evaluate('def s = System; s.exit(0)')
+        }
+        shouldFail(SecurityException) {
+            evaluate('Script t = this; t.evaluate("System.exit(0)")')
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java
new file mode 100644
index 0000000..848ebf3
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DefaultGrailsDomainClassInjector.java
@@ -0,0 +1,264 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.grails.compiler.injection;
+
+//import org.apache.commons.logging.Log;
+//import org.apache.commons.logging.LogFactory;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * This is substantially the same code from Grails, except some references de-referenced
+ * and the macro class added.
+ *
+ * Default implementation of domain class injector interface that adds the 'id'
+ * and 'version' properties and other previously boilerplate code
+ *
+ * @author Graeme Rocher
+ *
+ * @since 0.2
+ *
+ * Created: 20th June 2006
+ */
+@GroovyASTTransformation(phase= CompilePhase.CANONICALIZATION)
+public class DefaultGrailsDomainClassInjector implements ASTTransformation {
+        //GrailsDomainClassInjector {
+
+    //private static final Log LOG  = LogFactory.getLog(DefaultGrailsDomainClassInjector.class);
+
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        performInjection((ClassNode) nodes[1]);
+    }
+
+    public void performInjection(ClassNode classNode) {
+        if(shouldInject(classNode)) {
+            injectIdProperty(classNode);
+
+            injectVersionProperty(classNode);
+
+            injectToStringMethod(classNode);
+
+            injectAssociations(classNode);
+
+        }
+    }
+
+    public boolean shouldInject(URL url) {
+        return true; //return GrailsResourceUtils.isDomainClass(url);
+    }
+
+    private boolean shouldInject(ClassNode classNode) {
+        //String fullName = GrailsASTUtils.getFullName(classNode);
+        //String mappingFile = GrailsDomainConfigurationUtil.getMappingFileName(fullName);
+
+        //if(getClass().getResource(mappingFile)!=null) {
+            //if(LOG.isDebugEnabled()) {
+                //LOG.debug("[GrailsDomainInjector] Mapping file ["+mappingFile+"] found. Skipping property injection.");
+            //}
+            //return false;
+        //}
+        return true;
+    }
+
+    private void injectAssociations(ClassNode classNode) {
+
+        List properties = classNode.getProperties();
+        List propertiesToAdd = new ArrayList();
+        for (Iterator p = properties.iterator(); p.hasNext();) {
+            PropertyNode pn = (PropertyNode) p.next();
+            final boolean isHasManyProperty = pn.getName().equals(/*GrailsDomainClassProperty.*/RELATES_TO_MANY) || pn.getName().equals(/*GrailsDomainClassProperty.*/HAS_MANY);
+            if(isHasManyProperty) {
+                Expression e = pn.getInitialExpression();
+                propertiesToAdd.addAll(createPropertiesForHasManyExpression(e,classNode));
+            }
+            final boolean isBelongsTo = pn.getName().equals(/*GrailsDomainClassProperty.*/BELONGS_TO);
+            if(isBelongsTo) {
+                Expression e = pn.getInitialExpression();
+                propertiesToAdd.addAll(createPropertiesForBelongsToExpression(e,classNode));
+            }
+        }
+        injectAssociationProperties(classNode,propertiesToAdd);
+    }
+
+    private Collection createPropertiesForBelongsToExpression(Expression e, ClassNode classNode)
+    {
+        List properties = new ArrayList();
+        if(e instanceof MapExpression) {
+            MapExpression me = (MapExpression)e;
+            List mapEntries = me.getMapEntryExpressions();
+            for (Iterator i = mapEntries.iterator(); i.hasNext();) {
+                MapEntryExpression mme = (MapEntryExpression) i.next();
+                String key = mme.getKeyExpression().getText();
+
+                String type = mme.getValueExpression().getText();
+
+                properties.add(new PropertyNode(key,Modifier.PUBLIC, ClassHelper.make(type) , classNode, null,null,null));
+            }
+        }
+
+        return properties;
+    }
+
+    private void injectAssociationProperties(ClassNode classNode, List propertiesToAdd) {
+        for (Iterator i = propertiesToAdd.iterator(); i.hasNext();) {
+            PropertyNode pn = (PropertyNode) i.next();
+            if(!/*GrailsASTUtils.*/hasProperty(classNode, pn.getName())) {
+                //if(LOG.isDebugEnabled()) {
+                //    LOG.debug("[GrailsDomainInjector] Adding property [" + pn.getName() + "] to class [" + classNode.getName() + "]");
+                //}
+                classNode.addProperty(pn);
+            }
+        }
+    }
+
+    private List createPropertiesForHasManyExpression(Expression e, ClassNode classNode) {
+        List properties = new ArrayList();
+        if(e instanceof MapExpression) {
+            MapExpression me = (MapExpression)e;
+            List mapEntries = me.getMapEntryExpressions();
+            for (Iterator j = mapEntries.iterator(); j.hasNext();) {
+                MapEntryExpression mee = (MapEntryExpression) j.next();
+                Expression keyExpression = mee.getKeyExpression();
+                String key = keyExpression.getText();
+                addAssociationForKey(key,properties,classNode);
+            }
+        }
+        return properties;
+    }
+
+    private void addAssociationForKey(String key, List properties, ClassNode classNode) {
+            properties.add(new PropertyNode(key, Modifier.PUBLIC, new ClassNode(Set.class), classNode,null, null, null));
+    }
+
+    private void injectToStringMethod(ClassNode classNode) {
+        final boolean hasToString = /*GrailsASTUtils.*/implementsZeroArgMethod(classNode, "toString");
+
+        if(!hasToString) {
+            GStringExpression ge = new GStringExpression(classNode.getName() + " : ${id}");
+            ge.addString(new ConstantExpression(classNode.getName()+" : "));
+            ge.addValue(new VariableExpression("id"));
+            Statement s = new ReturnStatement(ge);
+            MethodNode mn = new MethodNode("toString",Modifier.PUBLIC,new ClassNode(String.class), new Parameter[0],new ClassNode[0],s);
+            //if(LOG.isDebugEnabled()) {
+            //    LOG.debug("[GrailsDomainInjector] Adding method [toString()] to class [" + classNode.getName() + "]");
+            //}
+            classNode.addMethod(mn);
+        }
+    }
+
+    private void injectVersionProperty(ClassNode classNode) {
+        final boolean hasVersion = /*GrailsASTUtils.*/hasProperty(classNode, /*GrailsDomainClassProperty.*/VERSION);
+
+        if(!hasVersion) {
+            //if(LOG.isDebugEnabled()) {
+            //    LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.VERSION + "] to class [" + classNode.getName() + "]");
+            //}
+            classNode.addProperty( /*GrailsDomainClassProperty.*/VERSION, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
+        }
+    }
+
+    private void injectIdProperty(ClassNode classNode) {
+        final boolean hasId = /*GrailsASTUtils.*/hasProperty(classNode,/*GrailsDomainClassProperty.*/IDENTITY);
+
+        if(!hasId) {
+            //if(LOG.isDebugEnabled()) {
+            //    LOG.debug("[GrailsDomainInjector] Adding property [" + GrailsDomainClassProperty.IDENTITY + "] to class [" + classNode.getName() + "]");
+            //}
+            classNode.addProperty( /*GrailsDomainClassProperty.*/IDENTITY, Modifier.PUBLIC, new ClassNode(Long.class), null, null, null);
+        }
+    }
+
+
+    //***************************************************************
+    // from GrailsASTUtils
+    //***************************************************************
+    /**
+     * Returns whether a classNode has the specified property or not
+     *
+     * @param classNode The ClassNode
+     * @param propertyName The name of the property
+     * @return True if the property exists in the ClassNode
+     */
+    public static boolean hasProperty(ClassNode classNode, String propertyName) {
+        if(classNode == null || propertyName == null || "".equals(propertyName.trim()))
+            return false;
+
+        List properties = classNode.getProperties();
+        for (Iterator i = properties.iterator(); i.hasNext();) {
+            PropertyNode pn = (PropertyNode) i.next();
+            if(pn.getName().equals(propertyName))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tests whether the ClasNode implements the specified method name
+     *
+     * @param classNode The ClassNode
+     * @param methodName The method name
+     * @return True if it does implement the method
+     */
+    public static boolean implementsZeroArgMethod(ClassNode classNode, String methodName) {
+        return implementsMethod(classNode, methodName, new Class[0]);
+    }
+
+
+    /**
+     * Tests whether the ClassNode implements the specified method name
+     *
+     * @param classNode The ClassNode
+     * @param methodName The method name
+     * @param argTypes
+     * @return True if it implements the method
+     */
+    private static boolean implementsMethod(ClassNode classNode, String methodName, Class[] argTypes) {
+        List methods = classNode.getMethods();
+        if (argTypes == null || argTypes.length ==0) {
+            for (Iterator i = methods.iterator(); i.hasNext();) {
+                MethodNode mn = (MethodNode) i.next();
+                boolean methodMatch = mn.getName().equals(methodName);
+                if(methodMatch)return true;
+                // TODO Implement further parameter analysis
+            }
+        }
+        return false;
+    }
+
+    //***************************************************************
+    // from GrailsDomainClassProperty
+    //***************************************************************
+    private static final String RELATES_TO_MANY = "relatesToMany";
+    private static final String BELONGS_TO = "belongsTo";
+    private static final String HAS_MANY = "hasMany";
+    private static final String IDENTITY = "id";
+    private static final String VERSION = "version";
+}
diff --git a/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java
new file mode 100644
index 0000000..3494ede
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/grails/compiler/injection/DomainClass.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.grails.compiler.injection;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Danno
+ * Date: Jan 30, 2008
+ * Time: 8:11:08 PM
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+@GroovyASTTransformationClass("org.codehaus.groovy.grails.compiler.injection.DefaultGrailsDomainClassInjector")
+public @interface DomainClass {
+}
diff --git a/src/main/groovy/osgi/build.properties b/src/main/groovy/osgi/build.properties
new file mode 100644
index 0000000..bb104c4
--- /dev/null
+++ b/src/main/groovy/osgi/build.properties
@@ -0,0 +1,28 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+# groovy bin dir is required to locate the groovy jar 
+# in case it's not on the classpath
+groovy.bin.dir=../../../target/dist
+
+# version is required to locate jar file and build jar manifest
+groovy.version=1.7-beta-1-SNAPSHOT
+
+# osgi jar file location must be specified so code compiles
+osgi.jar=../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar
diff --git a/src/main/groovy/osgi/build.xml b/src/main/groovy/osgi/build.xml
new file mode 100644
index 0000000..9d31efb
--- /dev/null
+++ b/src/main/groovy/osgi/build.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0"?>
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<project name="groovy-osgi-sample" default="print-results">
+
+  <property name="build.dir" value="build"/>
+  <property file="build.properties"/>
+  <property name="groovy.jar" value="groovy-${groovy.version}.jar" />
+  <property name="hello.bundle" value="hello-bundle-imports-groovy.jar" />
+  <property name="hello.bundle.contains" value="hello-bundle-contains-groovy.jar" />
+  <property name="harness.bundle" value="hello-groovy-test-harness.jar" />
+    
+  <taskdef name="groovyc"
+	   classname="org.codehaus.groovy.ant.Groovyc"
+	   classpathref="project.classpath"/>
+  
+  <target name="init" description="cleanup and reinitialize">
+	  <path id="project.classpath">
+		  <pathelement location="${osgi.jar}"/>
+		  <pathelement location="${groovy.bin.dir}${file.separator}${groovy.jar}"/>
+		  <pathelement location="${build.dir}${file.separator}${hello.bundle}"/>
+	  </path>
+	  
+	  <delete dir="${build.dir}" />
+	  <mkdir dir="${build.dir}${file.separator}hello-groovy-bundle" />
+	  <mkdir dir="${build.dir}${file.separator}hello-groovy-test-harness" />
+  </target>
+  
+  <target name="make-hello-groovy-bundle" description="compile and build the Hello Groovy bundles">
+  
+    <groovyc destdir="${build.dir}${file.separator}hello-groovy-bundle"
+	     srcdir=".${file.separator}hello-groovy-bundle"
+              listfiles="true">
+              <classpath refid="project.classpath"/>
+    </groovyc>
+    
+    <!-- This jar file imports Groovy from the container -->
+    <jar destfile="${build.dir}${file.separator}${hello.bundle}">
+	    <fileset dir="${build.dir}${file.separator}hello-groovy-bundle"/>
+	    <manifest>
+		    <attribute name="Built-By" value="${user.name}"/>
+		    <attribute name="provider" value="org.codehaus.groovy.osgi"/>
+		    <attribute name="Bundle-ManifestVersion" value="2"/>
+		    <attribute name="Bundle-Name" value="Groovy OSGi Example Bundle"/>
+		    <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.hello-groovy-bundle"/>
+		    <attribute name="Bundle-Version" value="1.0.0"/>
+		    <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.Activator"/>
+		    <attribute name="Bundle-Vendor" value="Groovy"/>
+		    <attribute name="Bundle-Localization" value="plugin"/>
+		    <attribute name="Import-Package" value="groovy.lang;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.reflection;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.runtime;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.runtime.callsite;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.w3c.dom,org.osgi.framework;version=&quot;1.3.0&quot;"/> 
+		    <attribute name="Export-Package" value="org.codehaus.groovy.osgi;version=&quot;1.0.0&quot;"/> 
+		    <attribute name="Bundle-ClassPath" value="."/>
+	    </manifest>
+    </jar>
+    
+    <!-- This jar includes the Groovy jar within itself-->
+    <jar destfile="${build.dir}${file.separator}${hello.bundle.contains}">
+	    <fileset dir="${build.dir}${file.separator}hello-groovy-bundle"/>
+	    <fileset file="${groovy.bin.dir}${file.separator}${groovy.jar}" casesensitive="no" />
+	    <manifest>
+		    <attribute name="Built-By" value="${user.name}"/>
+		    <attribute name="provider" value="org.codehaus.groovy.osgi"/>
+		    <attribute name="Bundle-ManifestVersion" value="2"/>
+		    <attribute name="Bundle-Name" value="Groovy OSGi Example Bundle"/>
+		    <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.hello-groovy-bundle"/>
+		    <attribute name="Bundle-Version" value="1.0.0"/>
+		    <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.Activator"/>
+		    <attribute name="Bundle-Vendor" value="Groovy"/>
+		    <attribute name="Bundle-Localization" value="plugin"/>
+		    <attribute name="Import-Package" value="org.w3c.dom,org.osgi.framework;version=&quot;1.3.0&quot;"/> 
+		    <attribute name="Export-Package" value="org.codehaus.groovy.osgi;version=&quot;1.0.0&quot;"/> 
+		    <attribute name="Bundle-ClassPath" value=".,${groovy.jar}"/>
+	    </manifest>
+    </jar> 
+  </target>
+
+  <target name="make-harness-bundle" description="Makes the test harness bundle">
+	  
+	  <groovyc destdir="${build.dir}${file.separator}hello-groovy-test-harness"
+		   srcdir=".${file.separator}hello-groovy-test-harness"
+		   listfiles="true">
+		  <classpath refid="project.classpath"/>
+	  </groovyc>
+	  
+	  <jar destfile="${build.dir}${file.separator}${harness.bundle}">
+		  <fileset dir="${build.dir}${file.separator}hello-groovy-test-harness"/>
+		  <manifest>
+			  <attribute name="Built-By" value="${user.name}"/>
+			  <attribute name="provider" value="org.codehaus.groovy.osgi.harness"/>
+			  <attribute name="Bundle-ManifestVersion" value="2"/>
+			  <attribute name="Bundle-Name" value="Groovy OSGi Test Harness"/>
+			  <attribute name="Bundle-SymbolicName" value="org.codehaus.groovy.osgi.harness.hello-groovy-test-harness"/>
+			  <attribute name="Bundle-Version" value="1.0.0"/>
+			  <attribute name="Bundle-Activator" value="org.codehaus.groovy.osgi.harness.HarnessActivator"/>
+			  <attribute name="Bundle-Vendor" value="Groovy"/>
+			  <attribute name="Bundle-Localization" value="plugin"/>
+			  <attribute name="Import-Package" value="org.codehaus.groovy.runtime.typehandling;version=&quot;1.0.0&quot;,org.codehaus.groovy.osgi;version=&quot;1.0.0&quot;,groovy.lang;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.reflection;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.runtime;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.codehaus.groovy.runtime.callsite;version=&quot;1.7.0.beta-1-SNAPSHOT&quot;,org.w3c.dom,org.osgi.framework;version=&quot;1.3.0&quot;"/> 
+			  <attribute name="Bundle-ClassPath" value="."/>
+		  </manifest>
+	  </jar>
+	  
+  </target>
+
+  <target name="print-results" depends="init,make-hello-groovy-bundle,make-harness-bundle" description="compile and build everything">
+	<makeurl file="${basedir}/${groovy.bin.dir}/${groovy.jar}" property="groovy.jar.url"/>
+	<makeurl file="${basedir}/${build.dir}/${hello.bundle}" property="hello.jar1.url"/>
+	<makeurl file="${basedir}/${build.dir}/${hello.bundle.contains}" property="hello.jar2.url"/>
+	<makeurl file="${basedir}/${build.dir}/${harness.bundle}" property="harness.jar.url"/>
+	
+	<echo>To run the OSGi console, run the following command: 
+ java -jar ${osgi.jar} -console 
+To install these applications in the container, run the following commands in the OSGi container: 
+ install ${groovy.jar.url}
+ install ${hello.jar1.url}
+ install ${hello.jar2.url}
+ install ${harness.jar.url}
+To start the applications in the container, run the following commands in the OSGi container: 
+ start [bundle1] [bundle2]
+Where [bundle1] and [bundle] are the bundle IDs printed by the console in the previous step.</echo>  
+  </target>
+</project>
+
diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy
new file mode 100644
index 0000000..e250ec1
--- /dev/null
+++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/Activator.groovy
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.osgi
+
+import org.osgi.framework.BundleActivator
+import org.osgi.framework.BundleContext
+import org.osgi.framework.ServiceRegistration;
+
+/**
+* This is the OSGi Activator for the Groovy example bundles. 
+* Two things happen when the container starts this bundle: 
+*   1) a message is printed to standard out
+*   2) a service of type GroovyGreeter is added to the context 
+* The service is unregistered when the bundle is stopped. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+public class Activator implements BundleActivator {
+
+    ServiceRegistration registration
+  
+    public void start(BundleContext context) {
+        println "Groovy BundleActivator started"
+  
+        // Normally, the classloader code would not need to be run when 
+        // adding a service to the context. However, this is required when
+        // adding a Groovy service because of the way Groovy uses class 
+        // loaders and reflection. 
+        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader()
+        try {
+            Thread.currentThread().setContextClassLoader(getClass().getClassLoader())
+            GroovyGreeter myService = new GroovyGreeterImpl()
+            registration = context.registerService(GroovyGreeter.class.getName(), myService, null)
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalClassLoader)
+        }
+    }
+  
+    public void stop(BundleContext context) {
+        println "Groovy BundleActivator stopped"
+        registration.unregister();
+    }
+}
diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy
new file mode 100644
index 0000000..4409cd4
--- /dev/null
+++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeter.groovy
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.osgi
+
+/**
+* Objects of this type know how to print simple messages to 
+* standard out. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+public interface GroovyGreeter {
+  
+    void sayHello(); 
+}
diff --git a/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy
new file mode 100644
index 0000000..209871e
--- /dev/null
+++ b/src/main/groovy/osgi/hello-groovy-bundle/org/codehaus/groovy/osgi/GroovyGreeterImpl.groovy
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.osgi
+
+/**
+* A simple POGO that prints a greeting to standard out. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+public class GroovyGreeterImpl implements GroovyGreeter {
+  
+    public void sayHello() {
+        println "Hello from the Groovy Greeter!"
+    }
+}
diff --git a/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy b/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy
new file mode 100644
index 0000000..adccf08
--- /dev/null
+++ b/src/main/groovy/osgi/hello-groovy-test-harness/org/codehaus/groovy/osgi/harness/HarnessActivator.groovy
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.osgi.harness
+
+import org.osgi.framework.BundleActivator
+import org.osgi.framework.BundleContext
+import org.osgi.framework.ServiceRegistration
+import org.osgi.framework.ServiceReference
+import org.codehaus.groovy.osgi.GroovyGreeter
+
+/**
+ * This OSGi Activator finds all registered services of type GroovyGreeter
+ * and then invokes the sayHello() method on any that it finds.
+ *
+ * @author Hamlet D'Arcy
+ */
+public class HarnessActivator implements BundleActivator {
+
+    public void start(BundleContext context) {
+        String serviceName = GroovyGreeter.class.getName()
+        ServiceReference[] references = context.getAllServiceReferences(serviceName, null)
+
+        println "${ references ? references.size() : 0 } GroovyGreeter services found."
+
+        references?.each { ServiceReference ref ->
+            Object serviceHandle = context.getService(ref)
+            GroovyGreeter service = (GroovyGreeter) serviceHandle
+            service.sayHello()
+        }
+    }
+
+    public void stop(BundleContext context) {
+    }
+}
diff --git a/src/main/groovy/osgi/readme.txt b/src/main/groovy/osgi/readme.txt
new file mode 100644
index 0000000..7ee164c
--- /dev/null
+++ b/src/main/groovy/osgi/readme.txt
@@ -0,0 +1,140 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+About this Example
+------------------
+This example demonstrates two different usages of OSGi. The "hello-groovy-bundle" 
+service demonstrates how to write an OSGi bundle using Groovy. It is a simple 
+service that does two things when started within an OSGi container: 1) it prints 
+out a message to the console, and 2) it adds a Groovy service to the OSGi context 
+that can be consumed by the second example.  
+
+The "hello-groovy-test-harness" service, also written in Groovy, demonstrates how 
+to import and use the previous "hello-groovy-bundle" OSGi service. It locates and 
+invokes the service from the first example, which results in a message being 
+written to the console. 
+
+
+Building this Example
+---------------------
+IMPORTANT: You must edit build.properties before building the example. There are 
+three properties that must be set in build.properties: 
+  groovy.bin.dir - The example requires you specify the location of your 
+                   groovy jar. This is how the build finds groovyc.
+  groovy.version - The example requires you specify the version of your groovy
+                   jar. This is so that the jar files can be built correctly. 
+  osgi.jar - The example requires you specify the location of the OSGi jar. 
+                   This is required to compile the code. 
+
+This example was tested using the OSGi jar from Equinox 3.4, the OSGi container 
+that ships with Eclipse. You can download the Equinox jar from the Equinox website
+or search for it within your Eclipse directories. The jar will have a name similar 
+to : org.eclipse.osgi_3.4.0.v20080605-1900.jar
+  
+Once these properties are set, simply run ant to build: 
+  
+  ant
+
+The build creates three jar files: 
+  hello-bundle-imports-groovy.jar - OSGi bundle written in Groovy that resolves the 
+        groovy Jar file from the container.
+  hello-bundle-contains-groovy.jar - OSGi bundle written in Groovy that resolves the
+        groovy Jar file from within itself. The container never sees Groovy.
+  hello-groovy-test-harness.jar - OSGi bundle that loads and tests one of the previous
+        two services. 
+  
+The build also prints out the file URLs of the jar files. You need these URLs to 
+run the example. Also printed to the console is the command to run the Equinox 
+container. The final output of the Ant script may look like this: 
+
+     [echo] To run the OSGi console, run the following command:
+     [echo]  java -jar ../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console
+     [echo] To install these applications in the container, run the following commands in the OSGi container:
+     [echo]  install file:/home/user/dev/groovy-core/target/dist/groovy-all-1.7.0.jar
+     [echo]  install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-imports-groovy.jar
+     [echo]  install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-contains-groovy.jar
+     [echo]  install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-groovy-test-harness.jar
+     [echo] To start the applications in the container, run the following commands in the OSGi container:
+     [echo]  start [bundle1] [bundle2]
+     [echo] Where [bundle1] and [bundle] are the bundle IDs printed by the console in the previous step.
+
+
+Running this Example
+--------------------
+To run the example you must start the OSGi container, install the services, and 
+start the services. 
+
+To start the Equinox container, invoke the OSGi jar using java: 
+  
+  java -jar ../../../../equinox-3.4/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console
+
+This opens an OSGi console. You should be presented with an OSGi prompt: 
+
+  osgi> 
+
+Type the command "ss" to get a system status: 
+
+  osgi> ss
+
+  Framework is launched.
+
+  id      State       Bundle
+  0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
+
+Install the three bundles using the "install" command and the file URLs of the 
+jars built by Ant. Remember, the Ant script printed the file URLs to the console 
+as part of the build (replace groovy-all with groovy for 2.5+).
+
+osgi> install file:/home/user/dev/groovy-core/target/dist/groovy-all-1.7.0.jar
+Bundle id is 1
+
+osgi> install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-bundle-imports-groovy.jar
+Bundle id is 2
+
+osgi> install file:/home/user/dev/groovy-core/src/examples/osgi/build/hello-groovy-test-harness.jar
+Bundle id is 3
+
+Run the ss command to verify the bundles loaded correctly: 
+
+
+osgi> ss
+
+Framework is launched.
+
+id      State       Bundle
+0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
+1       INSTALLED   groovy-all_1.7.0
+2       INSTALLED   org.codehaus.groovy.osgi.hello-groovy-bundle_1.0.0
+3       INSTALLED   org.codehaus.groovy.osgi.harness.hello-groovy-test-harness_1.0.0
+
+Start the bundles with the "start" command to see them working: 
+
+osgi> start 1 2 3
+Groovy BundleActivator started
+1 GroovyGreeter services found.
+Hello from the Groovy Greeter!
+
+As expected, bundle 2 printed out a message from an object implemented in Groovy, 
+and bundle 3 printed out a message from a service implemented in Groovy, which it 
+loaded as an OSGi service from the BundleContext. 
+
+You may wish to uninstall the services using the "uninstall" command: 
+
+  osgi> uninstall 3 2 1
+  Groovy BundleActivator stopped
diff --git a/src/main/groovy/searchEngine/Indexer.groovy b/src/main/groovy/searchEngine/Indexer.groovy
new file mode 100644
index 0000000..2709e6a
--- /dev/null
+++ b/src/main/groovy/searchEngine/Indexer.groovy
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import org.apache.lucene.analysis.standard.StandardAnalyzer
+import org.apache.lucene.document.Document
+import org.apache.lucene.document.Field
+import org.apache.lucene.index.IndexWriter
+import org.apache.lucene.util.Version
+import org.apache.lucene.index.IndexWriterConfig
+import org.apache.lucene.store.FSDirectory
+import org.apache.lucene.document.TextField
+import org.apache.lucene.document.StringField
+import static org.apache.lucene.document.Field.Store.*
+
+/**
+ * Indexer: traverses a file system and indexes .txt files
+ *
+ * @author Jeremy Rayner <groovy@ross-rayner.com>
+ * based on examples in the wonderful 'Lucene in Action' book
+ * by Erik Hatcher and Otis Gospodnetic (https://www.manning.com/books/lucene-in-action-second-edition)
+ *
+ * June 25th, 2013: Updated for Lucene 4.3.1
+ * requires a lucene-4.3.x.jar from http://lucene.apache.org
+ */
+
+if (args.size() != 2 ) {
+    throw new Exception("Usage: groovy -cp lucene-1.4.3.jar Indexer <index dir> <data dir>")
+}
+def indexDir = FSDirectory.open(new File(args[0])) // Create Lucene index in this directory
+def dataDir = new File(args[1]) // Index files in this directory
+
+def start = new Date().time
+def numIndexed = index(indexDir, dataDir)
+def end = new Date().time
+
+println "Indexing $numIndexed files took ${end - start} milliseconds"
+
+def index(indexDir, dataDir) {
+    if (!dataDir.exists() || !dataDir.directory) {
+        throw new IOException("$dataDir does not exist or is not a directory")
+    }
+    def config = new IndexWriterConfig(Version.LUCENE_43, new StandardAnalyzer(Version.LUCENE_43))
+    def writer = new IndexWriter(indexDir, config) // Create Lucene index
+
+    dataDir.eachFileRecurse {
+        if (it.name =~ /.txt$/) { // Index .txt files only
+            indexFile(writer,it)
+        }
+    }
+    def numIndexed = writer.numDocs()
+    writer.close() // Close index
+    return numIndexed
+}
+
+void indexFile(writer, f) {
+    if (f.hidden || !f.exists() || !f.canRead() || f.directory) { return }
+
+    println "Indexing $f.canonicalPath"
+    def doc = new Document()
+
+    // Construct a Field that is tokenized and indexed, but is not stored in the index verbatim.
+    doc.add(new TextField("contents", f.newReader()))
+
+    // Construct a Field that is not tokenized, but is indexed and stored.
+    doc.add(new StringField("filename",f.canonicalPath, YES))
+
+    writer.addDocument(doc) // Add document to Lucene index
+}
\ No newline at end of file
diff --git a/src/main/groovy/searchEngine/Searcher.groovy b/src/main/groovy/searchEngine/Searcher.groovy
new file mode 100644
index 0000000..4afd145
--- /dev/null
+++ b/src/main/groovy/searchEngine/Searcher.groovy
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import org.apache.lucene.analysis.standard.StandardAnalyzer
+import org.apache.lucene.queryparser.classic.QueryParser
+import org.apache.lucene.search.IndexSearcher
+import org.apache.lucene.store.FSDirectory
+import org.apache.lucene.util.Version
+import org.apache.lucene.index.DirectoryReader
+
+/**
+ * Searcher: searches a Lucene index for a query passed as an argument
+ *
+ * @author Jeremy Rayner <groovy@ross-rayner.com>
+ * based on examples in the wonderful 'Lucene in Action' book
+ * by Erik Hatcher and Otis Gospodnetic (https://www.manning.com/books/lucene-in-action-second-edition)
+ *
+ * June 25th, 2013: Updated for Lucene 4.3.1
+ * requires a lucene-4.x.x.jar from http://lucene.apache.org
+ */
+
+if (args.size() != 2) {
+    throw new Exception("Usage: groovy -cp lucene-4.3.1.jar Searcher <index dir> <query>")
+}
+def indexDir = new File(args[0]) // Index directory create by Indexer
+def q = args[1] // Query string
+
+if (!indexDir.exists() || !indexDir.directory) {
+    throw new Exception("$indexDir does not exist or is not a directory")
+}
+
+def fsDir = DirectoryReader.open(FSDirectory.open(indexDir))
+def is = new IndexSearcher(fsDir) // Open index
+
+def parser = new QueryParser(Version.LUCENE_43, "contents", new StandardAnalyzer(Version.LUCENE_43))
+def query = parser.parse(q) // Parse query
+def start = new Date().time
+def hits = is.search(query, 10) // Search index
+def end = new Date().time
+
+println "Found ${hits.totalHits} document(s) (in ${end - start} milliseconds) that matched query '$q':"
+
+hits.scoreDocs.each { scoreDoc ->
+    println(is.doc(scoreDoc.doc)["filename"]) // Retrieve matching document and display filename
+}
+fsDir.close()
diff --git a/src/main/groovy/swing/BindingExample.groovy b/src/main/groovy/swing/BindingExample.groovy
new file mode 100644
index 0000000..3cdcb60
--- /dev/null
+++ b/src/main/groovy/swing/BindingExample.groovy
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>
+ * @since Groovy 1.1
+ *
+ * The real interesting part of this example are in the three properties of button:
+ *
+ *           text: bind(source:textField, sourceProperty:'text'),
+ *           margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}),
+ *           enabled: bind(source:checkBox, sourceProperty:'selected')
+ *
+ * This is where the real magic goes on, causing the button to react to the changes
+ * in the source widgets values.
+ */
+ package swing
+
+import groovy.swing.SwingBuilder
+import java.awt.GridBagConstraints as gb
+import java.awt.Insets
+
+sb = SwingBuilder.build() {
+    frame = frame(defaultCloseOperation:javax.swing.JFrame.DISPOSE_ON_CLOSE) {
+        gridBagLayout()
+
+        label("Text:", anchor:gb.WEST, insets:[6,6,3,3] as Insets)
+        textField = textField("Change Me!", fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[6,3,3,6] as Insets)
+
+        label("Margin:", anchor:gb.WEST, insets:[3,6,3,3] as Insets)
+        slider = slider(value:5, fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets)
+
+        panel()
+        checkBox = checkBox("Enbled", anchor:gb.WEST, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets)
+
+        separator(fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER)
+
+        button(anchor:gb.CENTER, gridwidth:gb.REMAINDER, gridheight:gb.REMAINDER, weightx:1.0, weighty:1.0, insets:[3,6,6,6] as Insets,
+            text: bind(source:textField, sourceProperty:'text'),
+            margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}),
+            enabled: bind(source:checkBox, sourceProperty:'selected')
+        )
+    }
+}
+
+frame.pack()
+frame.setSize(frame.width + 100, frame.height + 200)
+frame.show()
diff --git a/src/main/groovy/swing/BloglinesClient.groovy b/src/main/groovy/swing/BloglinesClient.groovy
new file mode 100644
index 0000000..017ef0a
--- /dev/null
+++ b/src/main/groovy/swing/BloglinesClient.groovy
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/*
+ * BloglinesClient.groovy - an example of the Bloglines Web Services
+ *
+ * Written by Marc Hedlund <marc@precipice.org>, September 2004.
+ * 
+ * Mangled by John Wilson September 2004
+ *
+ * Small adaptions to JSR Version by Dierk Koenig, June 2005
+ *
+ * Used in Marc's article at:
+ *    http://www.oreillynet.com/pub/a/network/2004/09/28/bloglines.html
+ *
+ * Requirements:
+ *   - install Groovy as detailed at <http://groovy.codehaus.org/>.
+ *   - put commons-httpclient-3.0-rc3.jar into GROOVY_HOME/lib
+ *       see <http://jakarta.apache.org/commons/httpclient/>.
+ *       note: this is currently designed for HttpClient2.x and not HttpClient3.x
+ *
+ * To Launch:
+ *   groovy BloglinesClient.groovy
+ *
+ * This work is licensed under the Creative Commons Attribution
+ * License. To view a copy of this license, visit
+ * <http://creativecommons.org/licenses/by/2.0/> or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ */
+package swing
+
+import groovy.swing.SwingBuilder
+import java.awt.BorderLayout
+import javax.swing.JOptionPane
+import javax.swing.JSplitPane
+import javax.swing.JTree
+import javax.swing.ListSelectionModel
+import javax.swing.WindowConstants
+import javax.swing.tree.DefaultMutableTreeNode
+import javax.swing.tree.TreeSelectionModel
+import org.apache.commons.httpclient.HttpClient
+import org.apache.commons.httpclient.UsernamePasswordCredentials
+import org.apache.commons.httpclient.methods.GetMethod
+
+//Set up global variables and data types
+server = 'rpc.bloglines.com'
+
+class Feed {
+    def name;
+    def id;
+    def unread;
+
+    String toString() { (unread == "0" ? name : "${name} (${unread})") }
+}
+
+class Item {
+    def title;
+    def description;
+
+    String toString() { title }
+}
+
+// Ask the user for account information (using simple dialogs)
+email = JOptionPane.showInputDialog(null, "Email address:", "Log in to Bloglines",
+        JOptionPane.QUESTION_MESSAGE)
+password = JOptionPane.showInputDialog(null, "Password:", "Log in to Bloglines",
+        JOptionPane.QUESTION_MESSAGE)
+
+//Use HTTPClient for web requests since the server requires authentication
+client = new HttpClient()
+credentials = new UsernamePasswordCredentials(email, password)
+client.state.setCredentials("Bloglines RPC", server, credentials)
+
+abstractCallBloglines = { method, parameters ->
+    url = "http://${server}/${method}${parameters}"
+    try {
+        get = new GetMethod(url)
+        get.doAuthentication = true
+        client.executeMethod(get)
+        return get.responseBodyAsStream
+    } catch (Exception e) {
+        println "Error retrieving <${url}>: ${e}"
+    }
+}
+
+callBloglinesListsub = abstractCallBloglines.curry('listsubs', '')
+callBloglinesGetItems = abstractCallBloglines.curry('getitems')
+
+//Get the list of subscriptions and parse it into a GPath structure
+opml = new XmlSlurper().parse(callBloglinesListsub())
+
+//Descend into the subscription outline, adding to the feed tree as we go
+treeTop = new DefaultMutableTreeNode("My Feeds")
+parseOutline(opml.body.outline.outline, treeTop)
+
+def parseOutline(parsedXml, treeLevel) {
+    parsedXml.each { outline ->
+        if (outline['@xmlUrl'] != null) {  // this is an individual feed
+            feed = new Feed(name: outline['@title'], id: outline['@BloglinesSubId'],
+                    unread: outline['@BloglinesUnread'])
+            treeLevel.add(new DefaultMutableTreeNode(feed))
+        } else {  // this is a folder of feeds
+            folder = new DefaultMutableTreeNode(outline['@title'])
+            parseOutline(outline.outline, folder)
+            treeLevel.add(folder)
+        }
+    }
+}
+
+//Build the base user interface objects and configure them
+swing = new SwingBuilder()
+feedTree = new JTree(treeTop)
+itemList = swing.list()
+itemText = swing.textPane(contentType: 'text/html', editable: false)
+model = feedTree.selectionModel
+model.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
+itemList.selectionMode = ListSelectionModel.SINGLE_SELECTION
+
+//Set up the action closures that will react to user selections
+listItems = { feed ->
+    rssStream = callBloglinesGetItems("?s=${feed.id}&n=0")
+    if (rssStream != null) {
+        try {
+            rss = new XmlSlurper().parse(rssStream)
+            itemList.listData = rss.channel.item.collect(new Vector()) {
+                new Item(title: it.title, description: it.description)
+            }
+            feed.unread = "0"  // update the unread item count in the feed list
+        } catch (Exception e) {
+            println "Error during <${feed.name}> RSS parse: ${e}"
+        }
+    }
+}
+
+feedTree.valueChanged = { event ->
+    itemText.text = ""  // clear any old item text
+    node = (DefaultMutableTreeNode) feedTree.getLastSelectedPathComponent()
+    if (node != null) {
+        feed = node.userObject
+        if (feed instanceof Feed && feed.unread != "0") {
+            listItems(feed)
+        }
+    }
+}
+
+itemList.valueChanged = { event ->
+    item = event.source.selectedValue
+    if (item instanceof Item && item?.description != null) {
+        itemText.text = "<html><body>${item.description}</body></html>"
+    }
+}
+
+//Put the user interface together and display it
+gui = swing.frame(title: 'Bloglines Client', location: [100, 100], size: [800, 600],
+        defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) {
+    panel(layout: new BorderLayout()) {
+        splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, dividerLocation: 200) {
+            scrollPane {
+                widget(feedTree)
+            }
+            splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) {
+                scrollPane(constraints: BorderLayout.CENTER) {
+                    widget(itemList)
+                }
+                scrollPane(constraints: BorderLayout.CENTER) {
+                    widget(itemText)
+                }
+            }
+        }
+    }
+}
+
+gui.show()
diff --git a/src/main/groovy/swing/ModelNodeExample.groovy b/src/main/groovy/swing/ModelNodeExample.groovy
new file mode 100644
index 0000000..da52139
--- /dev/null
+++ b/src/main/groovy/swing/ModelNodeExample.groovy
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package swing
+
+import groovy.swing.SwingBuilder
+import static javax.swing.WindowConstants.*
+import static java.awt.GridBagConstraints.*
+
+def bean = new ObservableMap([name:'Alice', phone:'719-555-1212', addr:'42 Other Way'])
+
+SwingBuilder.build {
+ frame = frame(
+       pack:true, 
+       show:true,
+       defaultCloseOperation:DISPOSE_ON_CLOSE)
+ {
+  beanModel = model(bean, bind:false)
+
+  gridBagLayout()
+
+  label('Name:', constraints:gbc(insets:[6,6,3,3]))
+  textField(text:beanModel.name,
+            columns:20,
+            gridwidth:REMAINDER,
+            fill:HORIZONTAL,
+            weightx:1,
+            insets:[6,3,3,6])
+
+  label('Phone:', constraints:gbc(insets:[3,6,3,3]))
+  textField(text:beanModel.phone,
+            columns:20,
+            gridwidth:REMAINDER,
+            fill:HORIZONTAL,
+            weightx:1,
+            insets:[3,3,3,6])
+
+  label('Address:', constraints:gbc(insets:[3,6,3,3]))
+  textField(text:beanModel.addr,
+            columns:20,
+            gridwidth:REMAINDER,
+            fill:HORIZONTAL,
+            weightx:1,
+            insets:[3,3,3,6])
+
+  button('Reset', actionPerformed:{beanModel.update()}, 
+                  constraints:gbc(gridwidth:2, 
+                                  anchor:EAST, 
+                                  weightx:1, 
+                                  insets:[9,0,0,6]))
+  button('Submit', 
+         insets:[9,0,0,0],
+         actionPerformed: { 
+             beanModel.reverseUpdate()
+             output.text = ("name = '$bean.name'\nphone = '$bean.phone'\naddr = '$bean.addr'\n\n")
+         })
+
+  separator(gridwidth:REMAINDER,
+            fill:HORIZONTAL,
+            insets:[3,6,3,6])
+  label('Output:',
+        gridwidth:REMAINDER,
+        anchor:WEST,
+        insets:[3,6,3,6])
+  scrollPane(preferredSize:[100, 100], 
+             gridwidth:REMAINDER,
+             fill:BOTH,
+             weighty:1,
+             insets:[3,6,6,6])
+  {
+   output = textArea()
+  }
+ }
+}
diff --git a/src/main/groovy/swing/RegexCoach.groovy b/src/main/groovy/swing/RegexCoach.groovy
new file mode 100644
index 0000000..e282116
--- /dev/null
+++ b/src/main/groovy/swing/RegexCoach.groovy
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+// inspired by http://weitz.de/regex-coach/
+package swing
+
+import java.awt.*
+import java.awt.event.*
+import java.util.regex.*
+import javax.swing.*
+import javax.swing.text.DefaultHighlighter
+import groovy.swing.SwingBuilder
+
+// define the view
+def swing = new SwingBuilder()
+def gui = swing.frame(title: 'The Groovy Regex Coach', location: [20, 40], size: [600, 500], defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) {
+    panel(layout: new BorderLayout()) {
+        splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) {
+            panel(layout: new BorderLayout()) {
+                label(constraints: BorderLayout.NORTH, text: 'Regular expression:')
+                scrollPane(constraints: BorderLayout.CENTER) {textPane(id: 'regexPane')}
+                label(constraints: BorderLayout.SOUTH, id: 'regexStatus', text: ' ')
+            }
+            panel(layout: new BorderLayout()) {
+                label(constraints: BorderLayout.NORTH, text: 'Target string:')
+                scrollPane(constraints: BorderLayout.CENTER) {textPane(id: 'targetPane')}
+                panel(constraints: BorderLayout.SOUTH, layout: new BorderLayout()) {
+                    label(constraints: BorderLayout.NORTH, id: 'targetStatus', text: ' ')
+                    panel(constraints: BorderLayout.SOUTH, layout: new FlowLayout()) {
+                        button('<<-', id: 'scanLeft')
+                        button('->>', id: 'scanRight')
+                    }
+                }
+            }
+        }
+    }
+}
+def highlighter = new RegexHighlighter(swing: swing)
+swing.regexPane.addKeyListener(highlighter)
+swing.targetPane.addKeyListener(highlighter)
+swing.scanLeft.addActionListener(highlighter)
+swing.scanRight.addActionListener(highlighter)
+gui.show()
+
+class RegexHighlighter extends KeyAdapter implements ActionListener {
+    def swing // reference to the view
+    int scanIndex // how many times to execute matcher.find()
+    def orange = new DefaultHighlighter.DefaultHighlightPainter(Color.ORANGE)
+    def yellow = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW)
+    def red = new DefaultHighlighter.DefaultHighlightPainter(Color.RED)
+
+    // react to user actions
+
+    public void actionPerformed(ActionEvent event) {
+        if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)}
+        if (event.actionCommand == '->>') {scanIndex++}
+        doHighlights()
+    }
+
+    public void keyReleased(KeyEvent event) {
+        scanIndex = 0
+        doHighlights()
+    }
+
+    private resetView() {
+        swing.regexPane.highlighter.removeAllHighlights()
+        swing.targetPane.highlighter.removeAllHighlights()
+        swing.regexStatus.text = ' '
+        swing.targetStatus.text = ' '
+    }
+
+    // the main regex logic
+
+    private doHighlights() {
+        try {
+            resetView()
+            // note: get the text from the underlying document,
+            // otherwise carriage return/line feeds different when using the JTextPane text
+            def regex = swing.regexPane.document.getText(0, swing.regexPane.document.length)
+            def target = swing.targetPane.document.getText(0, swing.targetPane.document.length)
+
+            def matcher = (target =~ regex)
+
+            // scan past the matches before the match we want
+            int scan = 0
+            while (scan < scanIndex) {
+                matcher.find()
+                scan++
+            }
+            if (matcher.find()) {
+                // highlight any captured groups
+                int i = 0
+                while (i++ < matcher.groupCount()) {
+                    swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange)
+                }
+                // highlight whole match
+                swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow)
+                if (regex.length() != 0) {
+                    swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}."
+                }
+            } else { // not found
+                scanIndex = Math.max(scan - 1, 0)
+                if (scanIndex > 0) {doHighlights()}
+                swing.targetStatus.text = "No match."
+            }
+        } catch (PatternSyntaxException e) {
+            swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red)
+            swing.regexStatus.text = e.description
+        }
+    }
+}
diff --git a/src/main/groovy/swing/RegexCoachController.groovy b/src/main/groovy/swing/RegexCoachController.groovy
new file mode 100644
index 0000000..bf6cd9d
--- /dev/null
+++ b/src/main/groovy/swing/RegexCoachController.groovy
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package swing
+
+import groovy.swing.SwingBuilder
+import java.awt.Color
+import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
+import java.awt.event.KeyAdapter
+import java.awt.event.KeyEvent
+import java.util.regex.PatternSyntaxException
+import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter
+
+// inspired by http://weitz.de/regex-coach/
+
+// define the view
+def swing = new SwingBuilder()
+
+def gui = swing.build(RegexCoachView)
+
+def highlighter = new RegexHighliter(swing: swing)
+swing.regexPane.addKeyListener(highlighter)
+swing.targetPane.addKeyListener(highlighter)
+swing.scanLeft.addActionListener(highlighter)
+swing.scanRight.addActionListener(highlighter)
+gui.show()
+
+class RegexHighliter extends KeyAdapter implements ActionListener {
+    def swing // reference to the view
+    int scanIndex // how many times to execute matcher.find()
+    def orange = new DefaultHighlightPainter(Color.ORANGE)
+    def yellow = new DefaultHighlightPainter(Color.YELLOW)
+    def red = new DefaultHighlightPainter(Color.RED)
+
+    // react to user actions
+    public void actionPerformed(ActionEvent event) {
+        if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)}
+        if (event.actionCommand == '->>') {scanIndex++}
+        doHighlights()
+    }
+    public void keyReleased(KeyEvent event) {
+        scanIndex = 0
+        doHighlights()
+    }
+
+    private resetView() {
+        swing.regexPane.highlighter.removeAllHighlights()
+        swing.targetPane.highlighter.removeAllHighlights()
+        swing.regexStatus.text = ' '
+        swing.targetStatus.text = ' '
+    }
+
+    // the main regex logic
+    private doHighlights() {
+        try {
+            resetView()
+            // note: get the text from the underlying document,
+            // otherwise carriage return/line feeds different when using the JTextPane text
+            def regex = swing.regexPane.document.getText(0, swing.regexPane.document.length)
+            def target = swing.targetPane.document.getText(0, swing.targetPane.document.length)
+
+            def matcher = (target =~ regex)
+
+            // scan past the matches before the match we want
+            int scan = 0
+            while (scan < scanIndex) {
+                matcher.find()
+                scan++
+            }
+            if (matcher.find()) {
+                // highlight any captured groups
+                int i = 0
+                while (i++ < matcher.groupCount()) {
+                    swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange)
+                }
+                // highlight whole match
+                swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow)
+                if (regex.length() != 0) {
+                    swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}."
+                }
+            } else {// not found
+                scanIndex = Math.max(scan - 1, 0)
+                if (scanIndex > 0) {doHighlights()}
+                swing.targetStatus.text = "No match."
+            }
+        } catch (PatternSyntaxException e) {
+            swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red)
+            swing.regexStatus.text = e.description
+        }
+    }
+}
diff --git a/src/main/groovy/swing/RegexCoachView.groovy b/src/main/groovy/swing/RegexCoachView.groovy
new file mode 100644
index 0000000..37868ed
--- /dev/null
+++ b/src/main/groovy/swing/RegexCoachView.groovy
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package swing
+
+import static java.awt.BorderLayout.*
+import static javax.swing.JSplitPane.VERTICAL_SPLIT
+import static javax.swing.WindowConstants.EXIT_ON_CLOSE
+
+frame(title: 'The Groovy Regex Coach', location: [20, 40], size: [600, 500], defaultCloseOperation: EXIT_ON_CLOSE) {
+    panel {
+        borderLayout()
+        splitPane(orientation: VERTICAL_SPLIT, dividerLocation: 150) {
+            panel {
+                borderLayout()
+                label(constraints: NORTH, text: 'Regular expression:')
+                scrollPane(constraints: CENTER) {
+                    textPane(id: 'regexPane')
+                }
+                label(constraints: SOUTH, id: 'regexStatus', text: ' ')
+            }
+            panel {
+                borderLayout()
+                label(constraints: NORTH, text: 'Target string:')
+                scrollPane(constraints: CENTER) {
+                    textPane(id: 'targetPane')
+                }
+                panel(constraints: SOUTH) {
+                    borderLayout()
+                    label(constraints: NORTH, id: 'targetStatus', text: ' ')
+                    panel(constraints: SOUTH) {
+                        flowLayout()
+                        button('<<-', id: 'scanLeft')
+                        button('->>', id: 'scanRight')
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/swing/Widgets.groovy b/src/main/groovy/swing/Widgets.groovy
new file mode 100644
index 0000000..14266cf
--- /dev/null
+++ b/src/main/groovy/swing/Widgets.groovy
@@ -0,0 +1,220 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package swing
+
+import java.awt.Color
+import javax.swing.SwingConstants
+import javax.swing.WindowConstants
+import groovy.swing.SwingBuilder
+
+class Widgets {
+
+    def swing = new SwingBuilder()
+    def unownedDialog
+    def ownedDialog
+    
+
+    static void main(args) {
+        def demo = new Widgets()
+        demo.run()
+    }
+
+    def showUnownedDialog(event) {
+        unownedDialog.show();
+    }
+    
+    def showOwnedDialog(event) {
+        ownedDialog.show();
+    }
+
+    void run() {
+        unownedDialog = swing.dialog(
+            title:'unrooted dialog',
+            location: [200, 200],
+            pack:true,
+            defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+            ) {
+                label("I am unowned, but not unwanted");
+            }
+
+        def frame = swing.frame(
+            title:'FrameTitle',
+            location:[100,100],
+            size:[800,400],
+            defaultCloseOperation:WindowConstants.EXIT_ON_CLOSE) {
+
+            menuBar {
+                menu(text:'File') {
+                    menuItem() {
+                        action(name:'New', closure:{ println("clicked on the new menu item!") })
+                    }
+                    menuItem() {
+                        action(name:'Open', closure:{ println("clicked on the open menu item!") })
+                    }
+                    separator()
+                    menuItem() {
+                        action(name:'Save', enabled:false, closure:{ println("clicked on the Save menu item!") })
+                    }
+                }
+                menu(text:'Dialogs') {
+                    menuItem() {
+                        action(name:'Owned Dialog', closure: this.&showOwnedDialog)
+                    }
+                    menuItem() {
+                        action(name:'Unowned Dialog', closure: this.&showUnownedDialog)
+                    }
+                    def deeplyOwnedDialog = swing.dialog(
+                        title:'rooted dialog #2',
+                        location: [200, 200],
+                        pack:true,
+                        defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+                        ) {
+                        label("ownership is deep");
+                    }
+                    menuItem() {
+                        action(name:'Deeply Owned Dialog', closure: {deeplyOwnedDialog.show()} )
+                    }
+                }
+            }
+
+            tabbedPane() {
+
+                //colorChooser(
+                //    name:"Color Chooser",
+                //    color: 0xfeed42)
+
+                panel(name:"Formatted Text Fields") {
+                    gridLayout(columns: 2, rows: 0)
+                    label("Simple Constructor:")
+                    formattedTextField()
+                    label("Date Value")
+                    formattedTextField(value: new java.util.Date())
+                    label("Integer Value")
+                    formattedTextField(value: new java.lang.Integer(42))
+                    label("Date Format")
+                    formattedTextField(format: java.text.DateFormat.getDateInstance())
+                    label("Currency Format ")
+                    formattedTextField(format: new java.text.DecimalFormat("¤###.00;(¤###.00)"))
+                }
+
+                panel(name:"Sliders") {
+                    flowLayout()
+                    slider(minimum:-100, 
+                        maximum:100, 
+                        majorTickSpacing: 50,
+                        orientation: SwingConstants.VERTICAL, 
+                        paintLabels:true)
+                    slider(minimum:-100, 
+                        maximum:100, 
+                        orientation: SwingConstants.VERTICAL, 
+                        paintLabels:true,
+                        paintTicks:true,
+                        majorTickSpacing: 50,
+                        minorTickSpacing: 10,
+                        snapToTicks:true,
+                        paintTrack:true)
+                }
+
+                panel(name:"Spinners") {
+                    gridBagLayout()
+                    label(
+                        text:"Tempuature in London:",
+                        insets:[12, 12, 2, 2],
+                        anchor: EAST,
+                        gridx: 0)
+                    spinner(
+                        model:spinnerNumberModel(minimum:-10, 
+                            maximum: 40, 
+                            value:20,
+                            stepSize:5),
+                        insets:[12, 3, 2, 12],
+                        anchor: WEST,
+                        gridx: 1,
+                        fill: HORIZONTAL)
+                    label(
+                        text:"Baseball Leagues:",
+                        insets:[3, 12, 2, 2],
+                        anchor: EAST,
+                        gridx: 0)
+                    spinner(
+                        model:spinnerListModel(
+                            list: ["Major League", "AAA", "AA", "A", "Rookie", "Semi-Pro", "Rec A", "Rec B"],
+                            value: "AA"),
+                        insets:[3, 3, 2, 12],
+                        anchor: WEST,
+                        gridx: 1,
+                        fill: HORIZONTAL)
+                    label(
+                        text:"Today's Date:",
+                        insets:[3, 12, 2, 2],
+                        anchor: EAST,
+                        gridx: 0)
+                    spinner(
+                        model:spinnerDateModel(calendarField: Calendar.HOUR_OF_DAY),
+                        insets:[3, 3, 2, 12],
+                        anchor: WEST,
+                        gridx: 1,
+                        fill: HORIZONTAL)
+                }
+
+                panel(name:"Border Layout") {
+                    borderLayout()
+                    label(text:"Border Layout", 
+                          constraints:NORTH,
+                          horizontalAlignment:SwingConstants.CENTER)
+                    label(text:"South", 
+                          constraints:SOUTH,
+                          background:Color.YELLOW,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on south")
+                    label(text:"West", 
+                          constraints:WEST,
+                          background:Color.ORANGE,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on west")
+                    label(text:"East", 
+                          constraints:EAST,
+                          background:Color.GREEN,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on east")
+                    label(text:"Center", 
+                          constraints:CENTER,
+                          background:Color.WHITE,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"<html>This is not the tooltip you are looking for.<br><i>*waves hand*</i>")
+                }
+            }
+
+            ownedDialog = swing.dialog(
+                title:'rooted dialog',
+                location: [200, 200],
+                pack:true,
+                defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+                ) {
+                label("j00 h4v3 b33n 0wn3xed");
+            }
+        }        
+        frame.show()
+    }
+    
+}
diff --git a/src/main/groovy/swing/binding/caricature/Caricature.groovy b/src/main/groovy/swing/binding/caricature/Caricature.groovy
new file mode 100644
index 0000000..a37519c
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/Caricature.groovy
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package swing.binding.caricature
+
+import groovy.swing.SwingBuilder
+import java.awt.Color
+import javax.swing.border.TitledBorder
+
+SwingBuilder.build {
+    frame(pack:true, show:true,
+        defaultCloseOperation:javax.swing.JFrame.DISPOSE_ON_CLOSE)
+    {
+        borderLayout()
+        panel(constraints:CENTER,) {
+            // place in panel to insure square layout
+            caricature = widget(new JCaricature(
+                background:Color.WHITE, opaque:true))
+        }
+        hbox(constraints:SOUTH) {
+            panel(border:new TitledBorder("Style")) {
+                gridLayout(new java.awt.GridLayout(5, 2))
+                label("Eyes") // font stuff
+                eyeSlider = slider(minimum:0, maximum:4,
+                    value:bind(target:caricature,
+                        targetProperty:'eyeStyle',
+                        value:2))
+                label("Face") // font stuff
+                faceSlider = slider(minimum:0, maximum:4,
+                    value:bind(target:caricature,
+                        targetProperty:'faceStyle',
+                        value:2))
+                label("Mouth") // font stuff
+                mouthSlider = slider(minimum:0, maximum:4,
+                    value:bind(target:caricature,
+                        targetProperty:'mouthStyle',
+                        value:2))
+                label("Hair") // font stuff
+                hairSlider = slider(minimum:0, maximum:4,
+                    value:bind(target:caricature,
+                        targetProperty:'hairStyle',
+                        value:2))
+                label("Nose") // font stuff
+                noseSlider = slider(minimum:0, maximum:4,
+                    value:bind(target:caricature,
+                        targetProperty:'noseStyle',
+                        value:2))
+            }
+            panel(border:new TitledBorder("Effects")) {
+                gridLayout(new java.awt.GridLayout(2, 5))
+                label("Rotation") // font stuff
+                rotationSlider = slider(maximum:360,
+                    value:bind(target:caricature,
+                        targetProperty:'rotation',
+                        value:0))
+                label("Scale") // font stuff
+                scaleSlider = slider(maximum: 150, minimum:50,
+                    value:bind(target:caricature,
+                        targetProperty:'scale',
+                        converter: {it / 100f}, value:100))
+            }
+        }
+    }
+}
diff --git a/src/main/groovy/swing/binding/caricature/JCaricature.java b/src/main/groovy/swing/binding/caricature/JCaricature.java
new file mode 100644
index 0000000..0041862
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/JCaricature.java
@@ -0,0 +1,220 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/*
+ * Caricature.java
+ *
+ * Created on April 8, 2006, 4:09 PM
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+
+package swing.binding.caricature;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.swing.ImageIcon;
+import javax.swing.JPanel;
+
+/**
+ *
+ * @author sky
+ */
+public class JCaricature extends JPanel {
+    private Map/*<String,Image>*/ imageMap;
+
+    private boolean empty;
+    private int mouthStyle;
+    private int faceStyle;
+    private int hairStyle;
+    private int eyeStyle;
+    private int noseStyle;
+    private int rotation;
+    private float scale = 1.0f;
+
+    public JCaricature() {
+        if (imageMap == null) {
+            imageMap = new HashMap/*<String,Image>*/(1);
+            for (int i = 0; i < 5; i++) {
+                getImage("face", i);
+                getImage("hair", i);
+                getImage("eyes", i);
+                getImage("nose", i);
+                getImage("mouth", i);
+            }
+        }
+    }
+
+    public void setEmpty(boolean empty) {
+        if (this.empty != empty) {
+            this.empty = empty;
+            firePropertyChange("empty", !empty, empty);
+            repaint();
+        }
+    }
+
+    public boolean isEmpty() {
+        return empty;
+    }
+
+    public void setRotation(int rotation) {
+        int oldRotation = this.rotation;
+        this.rotation = rotation;
+        repaint();
+        firePropertyChange("rotation", oldRotation, rotation);
+    }
+
+    public int getRotation() {
+        return rotation;
+    }
+
+    public void setScale(float scale) {
+        float oldScale = this.scale;
+        this.scale = scale;
+        repaint();
+        firePropertyChange("scale", oldScale, scale);
+    }
+
+    public float getScale() {
+        return scale;
+    }
+
+    public void setMouthStyle(int style) {
+        int oldStyle = mouthStyle;
+        mouthStyle = style;
+        firePropertyChange("mouthStyle", oldStyle, style);
+        repaint();
+    }
+
+    public int getMouthStyle() {
+        return mouthStyle;
+    }
+
+    public void setFaceStyle(int style) {
+        int oldStyle = faceStyle;
+        faceStyle = style;
+        firePropertyChange("faceStyle", oldStyle, style);
+        repaint();
+    }
+
+    public int getFaceStyle() {
+        return faceStyle;
+    }
+
+    public void setHairStyle(int style) {
+        int oldStyle = hairStyle;
+        hairStyle = style;
+        firePropertyChange("hairStyle", oldStyle, style);
+        repaint();
+    }
+
+    public int getHairStyle() {
+        return hairStyle;
+    }
+
+    public void setEyeStyle(int style) {
+        int oldStyle = eyeStyle;
+        eyeStyle = style;
+        firePropertyChange("eyeStyle", oldStyle, style);
+        repaint();
+    }
+
+    public int getEyeStyle() {
+        return eyeStyle;
+    }
+
+    public void setNoseStyle(int style) {
+        int oldStyle = noseStyle;
+        noseStyle = style;
+        firePropertyChange("noseStyle", oldStyle, style);
+        repaint();
+    }
+
+    public int getNoseStyle() {
+        return noseStyle;
+    }
+
+    public Dimension getPreferredSize() {
+        if (!isPreferredSizeSet()) {
+            Image image = getImage("mouth", 0);
+            return new Dimension(image.getWidth(null), image.getHeight(null));
+        }
+        return super.getPreferredSize();
+    }
+
+    public Dimension getMaximumSize() {
+        if (!isMaximumSizeSet()) {
+            return getPreferredSize();
+        }
+        return super.getMaximumSize();
+    }
+
+    protected void paintComponent(Graphics g) {
+        super.paintComponent(g);
+        if (empty) {
+            return;
+        }
+        Graphics2D g2 = (Graphics2D)g.create();
+        Image image = getImage("face", getFaceStyle());
+        int iw = image.getWidth(null);
+        int ih = image.getHeight(null);
+//        g2.translate(iw / 2, ih / 2);
+        g2.translate(getWidth() / 2, getHeight() / 2);
+        if (iw != getWidth()) {
+            float forcedScale = (float)getWidth() / (float)iw;
+            g2.scale(forcedScale, forcedScale);
+        }
+        float scale = getScale();
+        if (scale != 1) {
+            g2.scale((double)scale, (double)scale);
+        }
+        int rotation = getRotation();
+        if (rotation != 0) {
+            g2.rotate(Math.toRadians(rotation));
+        }
+        drawImage(g2, "face", getFaceStyle());
+        drawImage(g2, "hair", getHairStyle());
+        drawImage(g2, "eyes", getEyeStyle());
+        drawImage(g2, "nose", getNoseStyle());
+        drawImage(g2, "mouth", getMouthStyle());
+        g2.dispose();
+    }
+
+    private void drawImage(Graphics g, String string, int i) {
+        Image image = getImage(string, i);
+        g.drawImage(image, -image.getWidth(null) / 2, -image.getHeight(null) / 2, null);
+    }
+
+    private Image getImage(String key, int style) {
+        String imageName = key + (style + 1) + ".gif";
+        Image image = (Image) imageMap.get(imageName);
+        if (image == null) {
+            System.err.println("name=" + imageName);
+            URL imageLoc = getClass().getResource("resources/" + imageName);
+            image = new ImageIcon(imageLoc).getImage();
+            imageMap.put(imageName, image);
+        }
+        return image;
+    }
+}
diff --git a/src/main/groovy/swing/binding/caricature/resources/eyes1.gif b/src/main/groovy/swing/binding/caricature/resources/eyes1.gif
new file mode 100644
index 0000000..54d0eb0
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/eyes1.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/eyes2.gif b/src/main/groovy/swing/binding/caricature/resources/eyes2.gif
new file mode 100644
index 0000000..4aa3091
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/eyes2.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/eyes3.gif b/src/main/groovy/swing/binding/caricature/resources/eyes3.gif
new file mode 100644
index 0000000..bfbbdea
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/eyes3.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/eyes4.gif b/src/main/groovy/swing/binding/caricature/resources/eyes4.gif
new file mode 100644
index 0000000..e25f652
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/eyes4.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/eyes5.gif b/src/main/groovy/swing/binding/caricature/resources/eyes5.gif
new file mode 100644
index 0000000..e5b6d6b
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/eyes5.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/face1.gif b/src/main/groovy/swing/binding/caricature/resources/face1.gif
new file mode 100644
index 0000000..01a107f
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/face1.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/face2.gif b/src/main/groovy/swing/binding/caricature/resources/face2.gif
new file mode 100644
index 0000000..0a8ea44
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/face2.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/face3.gif b/src/main/groovy/swing/binding/caricature/resources/face3.gif
new file mode 100644
index 0000000..e8c467b
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/face3.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/face4.gif b/src/main/groovy/swing/binding/caricature/resources/face4.gif
new file mode 100644
index 0000000..9364ab8
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/face4.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/face5.gif b/src/main/groovy/swing/binding/caricature/resources/face5.gif
new file mode 100644
index 0000000..e5f5e7c
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/face5.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/glasses.gif b/src/main/groovy/swing/binding/caricature/resources/glasses.gif
new file mode 100644
index 0000000..ef46483
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/glasses.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/glassesWEyes.gif b/src/main/groovy/swing/binding/caricature/resources/glassesWEyes.gif
new file mode 100644
index 0000000..93585b0
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/glassesWEyes.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/hair1.gif b/src/main/groovy/swing/binding/caricature/resources/hair1.gif
new file mode 100644
index 0000000..0466d00
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/hair1.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/hair2.gif b/src/main/groovy/swing/binding/caricature/resources/hair2.gif
new file mode 100644
index 0000000..47adf81
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/hair2.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/hair3.gif b/src/main/groovy/swing/binding/caricature/resources/hair3.gif
new file mode 100644
index 0000000..d93bd45
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/hair3.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/hair4.gif b/src/main/groovy/swing/binding/caricature/resources/hair4.gif
new file mode 100644
index 0000000..eca9d4b
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/hair4.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/hair5.gif b/src/main/groovy/swing/binding/caricature/resources/hair5.gif
new file mode 100644
index 0000000..147057a
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/hair5.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/mouth1.gif b/src/main/groovy/swing/binding/caricature/resources/mouth1.gif
new file mode 100644
index 0000000..a92e1d3
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/mouth1.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/mouth2.gif b/src/main/groovy/swing/binding/caricature/resources/mouth2.gif
new file mode 100644
index 0000000..1486022
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/mouth2.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/mouth3.gif b/src/main/groovy/swing/binding/caricature/resources/mouth3.gif
new file mode 100644
index 0000000..b6f5348
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/mouth3.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/mouth4.gif b/src/main/groovy/swing/binding/caricature/resources/mouth4.gif
new file mode 100644
index 0000000..937b46d
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/mouth4.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/mouth5.gif b/src/main/groovy/swing/binding/caricature/resources/mouth5.gif
new file mode 100644
index 0000000..f61a7a1
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/mouth5.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/nose1.gif b/src/main/groovy/swing/binding/caricature/resources/nose1.gif
new file mode 100644
index 0000000..336ea9f
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/nose1.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/nose2.gif b/src/main/groovy/swing/binding/caricature/resources/nose2.gif
new file mode 100644
index 0000000..5ab8609
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/nose2.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/nose3.gif b/src/main/groovy/swing/binding/caricature/resources/nose3.gif
new file mode 100644
index 0000000..ea3c643
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/nose3.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/nose4.gif b/src/main/groovy/swing/binding/caricature/resources/nose4.gif
new file mode 100644
index 0000000..832628b
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/nose4.gif
Binary files differ
diff --git a/src/main/groovy/swing/binding/caricature/resources/nose5.gif b/src/main/groovy/swing/binding/caricature/resources/nose5.gif
new file mode 100644
index 0000000..ec51067
--- /dev/null
+++ b/src/main/groovy/swing/binding/caricature/resources/nose5.gif
Binary files differ
diff --git a/src/main/groovy/swing/greet/Greet.groovy b/src/main/groovy/swing/greet/Greet.groovy
new file mode 100644
index 0000000..ca93900
--- /dev/null
+++ b/src/main/groovy/swing/greet/Greet.groovy
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/*
+ * Created by IntelliJ IDEA.
+ * User: Danno.Ferrin
+ * Date: Apr 26, 2008
+ * Time: 8:32:03 AM
+ */
+package groovy.swing.greet
+
+import groovy.beans.Bindable
+import groovy.swing.SwingBuilder
+import javax.swing.JOptionPane
+
+class Greet {
+
+    TwitterAPI api
+    Binding view
+
+    @Bindable boolean allowLogin = true
+    @Bindable boolean allowSelection = true
+    @Bindable boolean allowTweet = true
+    @Bindable def focusedUser = ""
+    @Bindable def friends  = []
+    @Bindable def tweets   = []
+    @Bindable def timeline = []
+    @Bindable def statuses = []
+
+    void startUp() {
+        setAllowSelection(false)
+        setAllowTweet(false)
+        view.greetFrame.show()
+        view.loginDialog.show()
+    }
+
+    void login(evt) {
+        setAllowLogin(false)
+        view.doOutside {
+            try {
+                if (api.login(view.twitterNameField.text, view.twitterPasswordField.password)) {
+                    setFriends(api.getFriends(api.authenticatedUser))
+                    friends.each {it.status.user = [screen_name:it.screen_name, profile_image_url:it.profile_image_url] }
+                    setStatuses(friends.collect {it.status})
+                    selectUser(api.authenticatedUser)
+                    view.greetFrame.show()
+                    view.loginDialog.dispose()
+                } else {
+                    JOptionPane.showMessageDialog(view.loginDialog, "Login failed")
+                }
+            } catch (Exception e) {
+                e.printStackTrace()
+            } finally {
+                view.edt {
+                    setAllowLogin(true)
+                    setAllowSelection(true)
+                    setAllowTweet(true)
+                }
+            }
+        }
+    }
+
+    void filterTweets(evt = null) {
+        setAllowSelection(false)
+        setAllowTweet(false)
+        view.doOutside {
+            try {
+                setStatuses(
+                    friends.collect {it.status}.findAll {it.text =~ view.searchField.text}
+                )
+                setTimeline(
+                    api.getFriendsTimeline(focusedUser).findAll {it.text =~ view.searchField.text}
+                )
+                setTweets(
+                    api.getTweets(focusedUser).findAll {it.text =~ view.searchField.text}
+                )
+            } catch (Exception e) {
+                e.printStackTrace()
+            } finally {
+                view.edt {
+                    setAllowSelection(true)
+                    setAllowTweet(true)
+                }
+            }
+        }
+    }
+
+    def userSelected(evt) {
+        view.doOutside {
+            selectUser(view.users.selectedItem)
+        }
+    }
+
+    def selectUser(user) {
+        setAllowSelection(false)
+        setAllowTweet(false)
+        try {
+            setFocusedUser(api.getUser(user.screen_name as String))
+            setTweets(api.getTweets(focusedUser).findAll {it.text =~ view.searchField.text})
+            setTimeline(api.getFriendsTimeline(focusedUser).findAll {it.text =~ view.searchField.text})
+        } finally {
+            view.edt {
+                setAllowSelection(true)
+                setAllowTweet(true)
+            }
+        }
+    }
+
+    def tweet(evt = null) {
+        setAllowTweet(false)
+        view.doOutside {
+            try {
+                api.tweet(view.tweetBox.text)
+                // true story: it froze w/o the EDT call here
+                view.edt {tweetBox.text = ""}
+                filterTweets()
+            } finally {
+                setAllowTweet(true)
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        def model = new TwitterAPI()
+        def controller = new Greet()
+        def view = new SwingBuilder()
+
+        controller.api = model
+        controller.view = view
+
+        view.controller = controller
+
+        view.build(View)
+        view.view = view
+
+        controller.startUp()
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/swing/greet/TwitterAPI.groovy b/src/main/groovy/swing/greet/TwitterAPI.groovy
new file mode 100644
index 0000000..76d13f4
--- /dev/null
+++ b/src/main/groovy/swing/greet/TwitterAPI.groovy
@@ -0,0 +1,162 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+ * Created by IntelliJ IDEA.
+ * User: Danno.Ferrin
+ * Date: Apr 25, 2008
+ * Time: 9:47:20 PM
+ */
+package groovy.swing.greet
+
+import groovy.beans.Bindable
+
+class TwitterAPI {
+
+    @Bindable String status = "\u00a0"
+    def authenticatedUser
+    XmlSlurper slurper = new XmlSlurper()
+    def imageMap = [:]
+
+    def withStatus(status, c) {
+        setStatus(status)
+        try {
+            def o = c()
+            setStatus("\u00a0")
+            return o
+        } catch (Throwable t) {
+            setStatus("Error $status : ${t.message =~ '400'?'Rate Limit Reached':t}")
+            throw t
+        }
+    }
+
+
+    boolean login(def name, def password) {
+        withStatus("Logging in") {
+            Authenticator.setDefault(
+                [getPasswordAuthentication : {
+                    return new PasswordAuthentication(name, password) }
+                ] as Authenticator)
+            authenticatedUser = getUser(name)
+            return true
+        }
+    }
+
+    def getFriends() {
+        getFriends(authenticatedUser)
+    }
+
+    def getFriends(String user) {
+        return getFriends(getUser(user))
+    }
+
+    def getFriends(user) {
+        def friends = [user]
+        withStatus("Loading Friends") {
+            def page = 1
+            def list = slurper.parse(new URL("http://twitter.com/statuses/friends/${user.screen_name}.xml").openStream())
+            while (list.length) {
+                list.user.collect(friends) {it}
+                page++
+                try {
+                  list = slurper.parse("http://twitter.com/statuses/friends/${user.screen_name}.xml&page=$page")
+                } catch (Exception e) { break }
+            }
+        }
+        withStatus("Loading Friends Images") {
+            return friends.each {
+                loadImage(it.profile_image_url as String)
+            }
+        }
+    }
+
+    def getFriendsTimeline() {
+        getFriendsTimeline(user)
+    }
+
+    def getFriendsTimeline(String friend) {
+        getFriendsTimeline(getUser(friend))
+    }
+
+    def getFriendsTimeline(user) {
+        def timeline = []
+        withStatus("Loading Timeline") {
+            timeline =  slurper.parse(
+                    new URL("http://twitter.com/statuses/friends_timeline/${user.screen_name}.xml").openStream()
+                ).status.collect{it}
+        }
+        withStatus("Loading Timeline Images") {
+            return timeline.each {
+                loadImage(it.user.profile_image_url as String)
+            }
+        }
+    }
+
+    def getTweets() {
+        return getTweets(user)
+    }
+
+    def getTweets(String friend) {
+        return getTweets(getUser(frield))
+    }
+
+    def getTweets(friend) {
+        def tweets = []
+        withStatus("Loading Tweets") {
+            tweets = slurper.parse(
+                    new URL("http://twitter.com/statuses/user_timeline/${friend.screen_name}.xml").openStream()
+                ).status.collect{it}
+        }
+        withStatus("Loading Tweet Images") {
+            return tweets.each {
+                loadImage(it.user.profile_image_url as String)
+            }
+        }
+    }
+
+    def getUser(String screen_name) {
+        withStatus("Loading User $screen_name") {
+            if (screen_name.contains('@')) {
+                return slurper.parse(
+                        new URL("http://twitter.com/users/show.xml?email=${screen_name}").openStream()
+                    )
+            } else {
+                return slurper.parse(
+                        new URL("http://twitter.com/users/show/${screen_name}.xml").openStream()
+                    )
+            }
+        }
+    }
+
+    def tweet(message) {
+        withStatus("Tweeting") {
+            def urlConnection = new URL("http://twitter.com/statuses/update.xml").openConnection()
+            urlConnection.doOutput = true
+            urlConnection.outputStream << "status=${URLEncoder.encode(message, 'UTF-8')}"
+            return slurper.parse(urlConnection.inputStream)
+        }
+    }
+
+    // no need to read these, swing seems to cache these so the EDT won't stall
+    def loadImage(image) {
+        if (!imageMap[image]) {
+            Thread.start {imageMap[image] = new javax.swing.ImageIcon(new URL(image))}
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/groovy/swing/greet/View.groovy b/src/main/groovy/swing/greet/View.groovy
new file mode 100644
index 0000000..3f8597c
--- /dev/null
+++ b/src/main/groovy/swing/greet/View.groovy
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+/**
+ * Created by IntelliJ IDEA.
+ * User: Danno.Ferrin
+ * Date: Apr 26, 2008
+ * Time: 8:31:21 AM
+ */
+package groovy.swing.greet
+
+import java.awt.Cursor
+import java.beans.PropertyChangeListener
+import javax.swing.*
+
+lookAndFeel('nimbus', 'mac', ['metal', [boldFonts: false]])
+
+actions() {
+    loginAction = action(
+        name: 'Login',
+        enabled: bind(source: controller, sourceProperty: 'allowLogin'),
+        closure: controller.&login
+    )
+
+    filterTweets = action(
+        name: 'Filter',
+        enabled: bind(source: controller, sourceProperty: 'allowSelection'),
+        closure: controller.&filterTweets
+    )
+
+    userSelected = action(
+        name: 'Select User',
+        enabled: bind(source: controller, sourceProperty: 'allowSelection'),
+        closure: controller.&userSelected
+    )
+
+    tweetAction = action(
+        name: 'Update',
+        enabled: bind(source: controller, sourceProperty: 'allowTweet'),
+        closure: controller.&tweet
+    )
+}
+
+tweetLineFont = new java.awt.Font("Ariel", 0, 12)
+tweetLine = panel(border: emptyBorder(3), preferredSize:[250,84]) {
+    gridBagLayout()
+    tweetIcon = label(verticalTextPosition:SwingConstants.BOTTOM,
+        horizontalTextPosition:SwingConstants.CENTER,
+        //anchor: BASELINE, insets: [3, 3, 3, 3])
+        anchor: CENTER, insets: [3, 3, 3, 3])
+    tweetText = textArea(rows: 4, lineWrap: true, wrapStyleWord: true,
+        opaque: false, editable: false, font: tweetLineFont,
+        gridwidth: REMAINDER, weightx: 1.0, fill: BOTH, insets: [3, 3, 3, 3])
+}
+tweetRenderer = {list, tweet, index, isSelected, isFocused ->
+    if (tweet?.user as String) {
+        tweetIcon.icon = controller.api.imageMap[tweet.user.profile_image_url as String]
+        tweetIcon.text = tweet.user.screen_name
+        tweetText.text = tweet.text
+    } else if (tweet?.text as String) {
+        tweetIcon.icon = controller.api.imageMap[tweet.parent().profile_image_url as String]
+        tweetIcon.text = tweet.parent().screen_name
+        tweetText.text = tweet.text
+    } else {
+        tweetIcon.icon = null
+        tweetIcon.text = null
+        tweetText.text = null
+    }
+    tweetLine
+} as ListCellRenderer
+
+
+userCell = label(border: emptyBorder(3))
+userCellRenderer = {list, user, index, isSelected, isFocused ->
+    if (user) {
+        userCell.icon = controller.api.imageMap[user.profile_image_url as String]
+        userCell.text = "<html>$user.screen_name<br>$user.name<br>$user.location<br>"
+    } else {
+        userCell.icon = null
+        userCell.text = null
+    }
+    userCell
+} as ListCellRenderer
+
+greetFrame = frame(title: "Greet - A Groovy Twitter Client",
+    defaultCloseOperation: javax.swing.JFrame.DISPOSE_ON_CLOSE, size: [320, 480],
+    locationByPlatform:true)
+{
+    panel(cursor: bind(source: controller, sourceProperty: 'allowSelection',
+        converter: {it ? null : Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)})
+    ) {
+
+        gridBagLayout()
+        users = comboBox(renderer: userCellRenderer, action: userSelected,
+            gridwidth: REMAINDER, insets: [6, 6, 3, 6], fill: HORIZONTAL)
+        label('Search:', insets: [3, 6, 3, 3])
+        searchField = textField(columns: 20, action: filterTweets,
+            insets: [3, 3, 3, 3], weightx: 1.0, fill: BOTH)
+        button(action: filterTweets,
+            gridwidth: REMAINDER, insets: [3, 3, 3, 6], fill:HORIZONTAL)
+        tabbedPane(gridwidth: REMAINDER, weighty: 1.0, fill: BOTH) {
+            scrollPane(title: 'Timeline') {
+                timelineList = list(visibleRowCount: 20, cellRenderer: tweetRenderer)
+            }
+            scrollPane(title: 'Tweets') {
+                tweetList = list(visibleRowCount: 20, cellRenderer: tweetRenderer)
+            }
+            scrollPane(title: 'Statuses') {
+                statusList = list(visibleRowCount: 20, cellRenderer: tweetRenderer)
+            }
+            // add data change listeners
+            [timeline:timelineList, tweets:tweetList, statuses:statusList].each {p, w ->
+                controller.addPropertyChangeListener(p,
+                    {evt -> w.listData = evt.newValue as Object[]} as PropertyChangeListener
+                )
+            }
+        }
+        separator(fill: HORIZONTAL, gridwidth: REMAINDER)
+        tweetBox = textField(action:tweetAction,
+            fill:BOTH, weightx:1.0, insets:[3,3,3,3], gridwidth:2)
+        tweetButton = button(tweetAction,
+            enabled:bind(source:tweetBox, sourceProperty:'text', converter:{it.length() < 140}),
+            gridwidth:REMAINDER, insets:[3,3,3,3])
+        separator(fill: HORIZONTAL, gridwidth: REMAINDER)
+        statusLine = label(text: bind(source: controller.api, sourceProperty: 'status'),
+            gridwidth: REMAINDER, insets: [3, 6, 3, 6], anchor: WEST
+        )
+    }
+
+
+    loginDialog = dialog(
+        title: "Login to Greet", pack: true, resizable: false,
+        defaultCloseOperation: WindowConstants.DISPOSE_ON_CLOSE,
+        locationByPlatform:true)
+    {
+        panel(border: emptyBorder(3),
+            cursor: bind(source: controller, sourceProperty: 'allowLogin',
+                converter: {it ? null : Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)})
+        ) {
+            gridBagLayout()
+            label("Username:",
+                anchor: EAST, insets: [3, 3, 3, 3])
+            twitterNameField = textField(action:loginAction, columns: 20,
+                gridwidth: REMAINDER, insets: [3, 3, 3, 3])
+            label("Password:",
+                anchor: EAST, insets: [3, 3, 3, 3])
+            twitterPasswordField = passwordField(action:loginAction, columns: 20,
+                gridwidth: REMAINDER, insets: [3, 3, 3, 3])
+            panel()
+            button(loginAction, defaultButton: true,
+                anchor: EAST, insets: [3, 3, 3, 3])
+        }
+    }
+}
+
+controller.addPropertyChangeListener("friends", {evt ->
+    view.edt { users.model = new DefaultComboBoxModel(evt.newValue as Object[]) }
+} as PropertyChangeListener)
+
+new Timer(120000, filterTweets).start()
diff --git a/src/main/groovy/swing/timelog/TimeLogMain.groovy b/src/main/groovy/swing/timelog/TimeLogMain.groovy
new file mode 100644
index 0000000..31acefa
--- /dev/null
+++ b/src/main/groovy/swing/timelog/TimeLogMain.groovy
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing.timelog
+
+import groovy.swing.SwingBuilder
+
+SwingBuilder swing = new SwingBuilder();
+swing.lookAndFeel('system')
+
+swing.model = new TimeLogModel()
+
+swing.actions() {
+    action(name:'Start', id:'startAction') {
+        stopButton.requestFocusInWindow()
+        doOutside {
+            model.startRecording(tfClient.text)
+        }
+    }
+    action(name:'Stop', id:'stopAction') {
+        doOutside {
+            model.stopRecording();
+            clientsTable.revalidate()
+        }
+    }
+}
+
+frame = swing.build(TimeLogView)
+frame.pack()
+frame.show()
+
diff --git a/src/main/groovy/swing/timelog/TimeLogModel.groovy b/src/main/groovy/swing/timelog/TimeLogModel.groovy
new file mode 100644
index 0000000..bb93772
--- /dev/null
+++ b/src/main/groovy/swing/timelog/TimeLogModel.groovy
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing.timelog
+
+import groovy.beans.Bindable
+
+class TimeLogRow {
+    String client
+    long start
+    long stop
+
+    long getDuration() {
+        return stop - start
+    }                                          
+}
+
+class TimeLogModel {
+
+    String currentClient
+    long currentStart
+    List<TimeLogRow> entries = []
+
+    @Bindable boolean running
+    @Bindable long elapsedTime
+
+    public synchronized startRecording(String client) {
+        if (running) throw new RuntimeException("Currently Running")
+        currentClient = client
+        currentStart = System.currentTimeMillis()
+        setRunning(true)
+
+        while (running) {
+            setElapsedTime(System.currentTimeMillis() - currentStart)
+            this.wait(1000)
+        }
+    }
+
+    public synchronized stopRecording() {
+        if (!running) throw new RuntimeException("Not Running")
+        setRunning(false)
+        this.notifyAll()
+        entries.add(new TimeLogRow(client:currentClient, start:currentStart, stop:System.currentTimeMillis()))
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/groovy/swing/timelog/TimeLogView.groovy b/src/main/groovy/swing/timelog/TimeLogView.groovy
new file mode 100644
index 0000000..91f711d
--- /dev/null
+++ b/src/main/groovy/swing/timelog/TimeLogView.groovy
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.swing.timelog
+
+import java.text.SimpleDateFormat
+import javax.swing.JFrame
+import java.awt.Font
+import java.awt.Color
+
+SimpleDateFormat timeFormat = new SimpleDateFormat('HH:mm:ss')
+timeFormat.setTimeZone(TimeZone.getTimeZone('GMT'))
+SimpleDateFormat dateFormat = new SimpleDateFormat('dd MMM yyyy HH:mm:ss')
+def convertTime = {it -> timeFormat.format new Date(it) }
+def convertDate = {it -> dateFormat.format new Date(it) }
+
+frame(title: 'Time Log Demo',
+    defaultCloseOperation : JFrame.EXIT_ON_CLOSE)
+{
+    gridBagLayout()
+
+    label('Client:', insets:[6,6,3,3])
+    textField('', id: 'tfClient',
+        enabled: bind( source: model, sourceProperty: 'running', converter: {!it} ),
+        gridwidth: REMAINDER, fill: HORIZONTAL, insets: [6,3,3,6])
+
+    label('00:00:00', font: new Font('Ariel', Font.BOLD, 42),
+        foreground: bind(source: model, sourceProperty: 'running', converter: {it ? Color.GREEN : Color.RED}),
+        text: bind(source: model, sourceProperty: 'elapsedTime', converter: convertTime),
+        gridwidth: 2, gridheight: 2, anchor: EAST, weightx: 1.0, insets: [3,6,3,3])
+    button(startAction, id: 'startButton',
+        enabled: bind(source: model, sourceProperty: 'running', converter: {!it}),
+        gridwidth: REMAINDER, insets: [3,3,3,6])
+    button(stopAction, id: 'stopButton',
+        enabled: bind(source: model, sourceProperty: 'running'),
+        gridwidth: REMAINDER, insets: [3,3,3,6])
+
+    separator(gridwidth: REMAINDER, fill: HORIZONTAL, insets: [9,6,9,6])
+
+    scrollPane(minimumSize: [100, 100],
+        gridwidth: REMAINDER, weighty: 1.0, fill: BOTH, insets: [3,6,6,6])
+    {
+        table(id: 'clientsTable') {
+            tableModel(list: model.entries) {
+                propertyColumn(header: 'Client',   propertyName: 'client')
+                closureColumn( header: 'Start',    read: {convertDate it.start})
+                closureColumn( header: 'Stop',     read: {convertDate it.stop})
+                closureColumn( header: 'Duration', read: {convertTime it.stop - it.start})
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/transforms/global/CompiledAtASTTransformation.groovy b/src/main/groovy/transforms/global/CompiledAtASTTransformation.groovy
new file mode 100644
index 0000000..f94e444
--- /dev/null
+++ b/src/main/groovy/transforms/global/CompiledAtASTTransformation.groovy
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.global
+
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.transform.*
+import org.codehaus.groovy.control.*
+import org.codehaus.groovy.ast.expr.*
+import org.codehaus.groovy.ast.stmt.*
+import java.lang.annotation.*
+import org.codehaus.groovy.ast.builder.AstBuilder
+
+/**
+* This ASTTransformation adds a static getCompiledTime() : String method to every class.  
+*
+* @author Hamlet D'Arcy
+*/ 
+@GroovyASTTransformation(phase=CompilePhase.CONVERSION)
+public class CompiledAtASTTransformation implements ASTTransformation {
+
+    private static final compileTime = new Date().toString()
+
+    public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
+
+        List classes = sourceUnit.ast?.classes
+        classes?.each { ClassNode clazz ->
+            clazz.addMethod(makeMethod())
+        }
+    }
+
+    /**
+    *  OpCodes should normally be referenced, but in a standalone example I don't want to have to include
+    * the jar at compile time. 
+    */ 
+    MethodNode makeMethod() {
+        def ast = new AstBuilder().buildFromSpec {
+            method('getCompiledTime', /*OpCodes.ACC_PUBLIC*/1 | /*OpCodes.ACC_STATIC*/8, String) {
+                parameters {}
+                exceptions {}
+                block { 
+                    returnStatement {
+                        constant(compileTime) 
+                    }
+                }
+                annotations {}
+            }
+        }
+        ast[0]
+    }
+}
diff --git a/src/main/groovy/transforms/global/CompiledAtExample.groovy b/src/main/groovy/transforms/global/CompiledAtExample.groovy
new file mode 100644
index 0000000..dffbf4f
--- /dev/null
+++ b/src/main/groovy/transforms/global/CompiledAtExample.groovy
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.global
+
+/**
+* Demonstrates how a global transformation works. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+
+println 'Script compiled at: ' + compiledTime
+
+class MyClass {
+    
+}
+
+println 'Class compiled at: ' + MyClass.compiledTime
diff --git a/src/main/groovy/transforms/global/CompiledAtIntegrationTest.groovy b/src/main/groovy/transforms/global/CompiledAtIntegrationTest.groovy
new file mode 100644
index 0000000..1f03f39
--- /dev/null
+++ b/src/main/groovy/transforms/global/CompiledAtIntegrationTest.groovy
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+import transforms.global.CompiledAtASTTransformation 
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.transform.*
+import org.codehaus.groovy.control.*
+import org.codehaus.groovy.tools.ast.*
+
+/**
+* This shows how to use the TransformTestHelper to test
+* a global transformation. It is a little hard to invoke 
+* because the CompiledAtASTTransformation must be on the 
+* classpath but the JAR containing the transform must not
+* or the transform gets applied twice. 
+*
+* @author Hamlet D'Arcy
+*/ 
+def transform = new CompiledAtASTTransformation()
+def phase = CompilePhase.CONVERSION
+def helper = new TransformTestHelper(transform, phase)
+def clazz = helper.parse(' class MyClass {} ' )
+assert clazz.getCompiledTime() != null
+println 'compiled at' + clazz.getCompiledTime()
diff --git a/src/main/groovy/transforms/global/LoggingASTTransformation.groovy b/src/main/groovy/transforms/global/LoggingASTTransformation.groovy
new file mode 100644
index 0000000..c6a2903
--- /dev/null
+++ b/src/main/groovy/transforms/global/LoggingASTTransformation.groovy
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.global
+
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.transform.GroovyASTTransformation
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import java.lang.annotation.Annotation
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+
+/**
+* This ASTTransformation adds a start and stop message to every single method call. 
+*
+* @author Hamlet D'Arcy
+*/ 
+@GroovyASTTransformation(phase=CompilePhase.CONVERSION)
+public class LoggingASTTransformation implements ASTTransformation {
+
+
+    public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
+        List methods = sourceUnit.getAST()?.getMethods()
+        methods?.each { MethodNode method ->
+            Statement startMessage = createPrintlnAst("Starting $method.name")
+            Statement endMessage = createPrintlnAst("Ending $method.name")
+
+            List existingStatements = method.getCode().getStatements()
+            existingStatements.add(0, startMessage)
+            existingStatements.add(endMessage)
+        }
+    }
+
+    /**
+    * Creates the AST for a println invocation. 
+    */ 
+    private Statement createPrintlnAst(String message) {
+        return new ExpressionStatement(
+            new MethodCallExpression(
+                new VariableExpression("this"),
+                new ConstantExpression("println"),
+                new ArgumentListExpression(
+                    new ConstantExpression(message)
+                )
+            )
+        )
+    }
+}
diff --git a/src/main/groovy/transforms/global/LoggingExample.groovy b/src/main/groovy/transforms/global/LoggingExample.groovy
new file mode 100644
index 0000000..2d9f38e
--- /dev/null
+++ b/src/main/groovy/transforms/global/LoggingExample.groovy
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.global
+
+/**
+* Demonstrates how a global transformation works. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+
+def greet() {
+    println "Hello World"
+}
+    
+// this prints out Hello World along with the extra compile time logging
+greet()
+
+
+//
+// The rest of this script is asserting that this all works correctly. 
+//
+
+// redirect standard out so we can make assertions on it
+def standardOut = new ByteArrayOutputStream();
+System.setOut(new PrintStream(standardOut)); 
+  
+greet()
+def result = standardOut.toString("ISO-8859-1").split('\n')
+assert "Starting greet"  == result[0].trim()
+assert "Hello World"     == result[1].trim()
+assert "Ending greet"    == result[2].trim()
+standardOut.close()
diff --git a/src/main/groovy/transforms/global/META-INF/services/org.codehaus.groovy.transform.ASTTransformation b/src/main/groovy/transforms/global/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
new file mode 100644
index 0000000..2de1e18
--- /dev/null
+++ b/src/main/groovy/transforms/global/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+transforms.global.LoggingASTTransformation
+transforms.global.CompiledAtASTTransformation
diff --git a/src/main/groovy/transforms/global/build.xml b/src/main/groovy/transforms/global/build.xml
new file mode 100644
index 0000000..66d16c3
--- /dev/null
+++ b/src/main/groovy/transforms/global/build.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<project name="groovy-global-ast-transformation-example" default="jar-transform">
+
+    <!-- necessary groovy jars are assumed to be on your classpath. -->
+    <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" />
+    
+    <target name="init" description="cleanup old class files">
+        <delete dir="examples"/>
+        <delete>
+            <fileset dir="." includes="**/*.jar"/>
+        </delete>        
+    </target>
+
+    <target name="compile-transform" depends="init" description="Compiles the AST Transformation">
+    
+        <groovyc destdir="."
+                srcdir="."
+                includes="*Transformation.groovy" 
+                listfiles="true">
+        </groovyc>
+        
+    </target>
+
+    <target name="jar-transform" depends="compile-transform" description="Creates a .jar file for the global transform" >
+        <jar destfile="LoggingTransform.jar"
+            basedir="."
+            includes="**/*.class,META-INF/**" />
+
+        <echo>You can now run "groovy -cp LoggingTransform.jar LoggingExample.groovy" or "groovy -cp LoggingTransform.jar CompiledAtExample.groovy" to see that the transformation worked.</echo>
+    </target>
+</project>
+
diff --git a/src/main/groovy/transforms/global/readme.txt b/src/main/groovy/transforms/global/readme.txt
new file mode 100644
index 0000000..e774984
--- /dev/null
+++ b/src/main/groovy/transforms/global/readme.txt
@@ -0,0 +1,48 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+Global AST Transformation Example
+
+This example shows how to wire together a global transformation. 
+
+The example requires ant in your path and the Groovy 1.6 (or greater) 
+Jar in your classpath. The current directory must *not* be on your
+classpath, otherwise ant will try to read the META-INF directory and
+apply the transformations prematurely. 
+
+To build the example run "ant" from the current directory. The default 
+target will compile the classes needed. The last step of the build 
+script prints out the command needed to run the example. 
+
+To run the first example perform the following from the command line: 
+  groovy -cp LoggingTransform.jar LoggingExample.groovy
+  
+The example should print: 
+  Starting greet
+  Hello World
+  Ending greet
+
+To run the second example perform the following from the command line: 
+  groovy -cp LoggingTransform.jar CompiledAtExample.groovy
+  
+The example should print: 
+  Scripted compiled at: [recently]
+  Class compiled at: [recently]
+
+No exceptions should occur. 
diff --git a/src/main/groovy/transforms/local/LoggingASTTransformation.groovy b/src/main/groovy/transforms/local/LoggingASTTransformation.groovy
new file mode 100644
index 0000000..bd19b95
--- /dev/null
+++ b/src/main/groovy/transforms/local/LoggingASTTransformation.groovy
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.local
+
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.transform.GroovyASTTransformation
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.stmt.Statement
+
+/**
+* This transformation finds all the methods defined in a script that have
+* the @WithLogging annotation on them, and then weaves in a start and stop 
+* message that is logged using println. 
+*
+* @author Hamlet D'Arcy
+*/ 
+@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
+public class LoggingASTTransformation implements ASTTransformation {
+
+    public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+        List methods = sourceUnit.getAST()?.getMethods()
+        // find all methods annotated with @WithLogging
+        methods.findAll { MethodNode method ->
+            method.getAnnotations(new ClassNode(WithLogging))
+        }.each { MethodNode method ->
+            Statement startMessage = createPrintlnAst("Starting $method.name")
+            Statement endMessage = createPrintlnAst("Ending $method.name")
+
+            List existingStatements = method.getCode().getStatements()
+            existingStatements.add(0, startMessage)
+            existingStatements.add(endMessage)
+        }
+    }
+
+    /**
+    * This creates the ASTNode for a println statement. 
+    */ 
+    private Statement createPrintlnAst(String message) {
+        return new ExpressionStatement(
+            new MethodCallExpression(
+                new VariableExpression("this"),
+                new ConstantExpression("println"),
+                new ArgumentListExpression(
+                    new ConstantExpression(message)
+                )
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/main/groovy/transforms/local/LoggingExample.groovy b/src/main/groovy/transforms/local/LoggingExample.groovy
new file mode 100644
index 0000000..4d47a64
--- /dev/null
+++ b/src/main/groovy/transforms/local/LoggingExample.groovy
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.local
+
+/**
+* Demonstrates how a local transformation works. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+
+def greet() {
+    println "Hello World"
+}
+    
+@WithLogging    //this should trigger extra logging
+def greetWithLogging() {
+    println "Hello World"
+}
+    
+// this prints out a simple Hello World
+greet()
+
+// this prints out Hello World along with the extra compile time logging
+greetWithLogging()
+
+
+//
+// The rest of this script is asserting that this all works correctly. 
+//
+
+// redirect standard out so we can make assertions on it
+def standardOut = new ByteArrayOutputStream();
+System.setOut(new PrintStream(standardOut)); 
+  
+greet()
+assert "Hello World" == standardOut.toString("ISO-8859-1").trim()
+
+// reset standard out and redirect it again
+standardOut.close()
+standardOut = new ByteArrayOutputStream();
+System.setOut(new PrintStream(standardOut)); 
+
+greetWithLogging()
+def result = standardOut.toString("ISO-8859-1").split('\n')
+assert "Starting greetWithLogging"  == result[0].trim()
+assert "Hello World"                == result[1].trim()
+assert "Ending greetWithLogging"    == result[2].trim()
+
diff --git a/src/main/groovy/transforms/local/WithLogging.groovy b/src/main/groovy/transforms/local/WithLogging.groovy
new file mode 100644
index 0000000..03611f2
--- /dev/null
+++ b/src/main/groovy/transforms/local/WithLogging.groovy
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package transforms.local
+import java.lang.annotation.Retention
+import java.lang.annotation.Target
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+import java.lang.annotation.ElementType
+import java.lang.annotation.RetentionPolicy
+
+/**
+* This is just a marker interface that will trigger a local transformation. 
+* The 3rd Annotation down is the important one: @GroovyASTTransformationClass
+* The parameter is the String form of a fully qualified class name. 
+*
+* @author Hamlet D'Arcy
+*/ 
+@Retention(RetentionPolicy.SOURCE)
+@Target([ElementType.METHOD])
+@GroovyASTTransformationClass(["transforms.local.LoggingASTTransformation"])
+public @interface WithLogging {
+}
\ No newline at end of file
diff --git a/src/main/groovy/transforms/local/build.xml b/src/main/groovy/transforms/local/build.xml
new file mode 100644
index 0000000..eef390a
--- /dev/null
+++ b/src/main/groovy/transforms/local/build.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<project name="groovy-local-ast-transformation-example" default="compile-transform">
+
+    <!-- necessary groovy jars are assumed to be on your classpath. -->
+    <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" />
+    
+    
+    <target name="init" description="cleanup old class files">
+        <delete dir="examples"/>
+    </target>
+
+    <target name="compile-transform" depends="init" description="Compiles the AST Transformation">
+    
+        <groovyc destdir="."
+                srcdir="."
+                includes="LoggingASTTransformation.groovy,WithLogging.groovy" 
+                listfiles="true">
+        </groovyc>
+        
+        <echo>You can now run "groovy LoggingExample.groovy" to see that the transformation worked.</echo>
+    </target>
+
+</project>
+
diff --git a/src/main/groovy/transforms/local/readme.txt b/src/main/groovy/transforms/local/readme.txt
new file mode 100644
index 0000000..2daade2
--- /dev/null
+++ b/src/main/groovy/transforms/local/readme.txt
@@ -0,0 +1,40 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+Local AST Transformation Example
+
+This example shows how to wire together a local transformation. 
+
+The example requires ant in your path and the Groovy 1.6 (or greater) 
+Jar in your classpath. 
+
+To build the example run "ant" from the current directory. The default 
+target will compile the classes needed. The last step of the build 
+script prints out the command needed to run the example. 
+
+To run the example perform the following from the command line: 
+  groovy LoggingExample.groovy
+  
+The example should print: 
+  Hello World
+  Starting greetWithLogging
+  Hello World
+  Ending greetWithLogging
+
+No exceptions should occur. 
\ No newline at end of file
diff --git a/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy
new file mode 100644
index 0000000..49e13e9
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy
@@ -0,0 +1,23 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+interface Animal {
+
+  String saySomething(String something);
+
+}
diff --git a/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy
new file mode 100644
index 0000000..1c58730
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package zoo
+
+abstract class Fish implements Animal {
+
+  String saySomething(String something) {
+    return "Blubb�: " + something + "..."
+  }
+
+}
diff --git a/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy
new file mode 100644
index 0000000..8f05925
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package zoo.fish
+
+import zoo.Fish
+
+class Shark extends Fish {
+
+  String saySomething(String something) {
+    return "Shark bites " + something + "...ROOOAR�!"
+  }
+
+}
diff --git a/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy
new file mode 100644
index 0000000..0b19518
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package zoo.fish
+
+class Trout extends zoo.Fish {
+
+  String saySomething(String something) {
+    return "Trout says " + something + "...blubb�!"
+  }
+
+}
diff --git a/src/main/groovy/webapps/groovlet-examples/WEB-INF/web.xml b/src/main/groovy/webapps/groovlet-examples/WEB-INF/web.xml
new file mode 100644
index 0000000..cd22859
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/WEB-INF/web.xml
@@ -0,0 +1,89 @@
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_3.xsd"
+    version="2.3">
+
+  <servlet>
+    <servlet-name>Groovlet</servlet-name>
+    <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
+    <init-param>
+      <param-name>verbose</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <init-param>
+      <param-name>resource.name.regex</param-name>
+      <param-value>none</param-value>
+    </init-param>
+    <init-param>
+      <param-name>resource.name.replacement</param-name>
+      <param-value>none</param-value>
+    </init-param>
+  </servlet>
+    
+  <servlet>
+    <servlet-name>Template|GSP</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+  </servlet>
+  
+  <servlet>
+    <servlet-name>Template|GString</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+    <init-param>
+      <param-name>template.engine</param-name>
+      <param-value>groovy.text.GStringTemplateEngine</param-value>
+    </init-param>
+  </servlet>
+  
+  <servlet>
+    <servlet-name>Template|XML</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+    <init-param>
+      <param-name>template.engine</param-name>
+      <param-value>groovy.text.XmlTemplateEngine</param-value>
+    </init-param>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>Template|GSP</servlet-name>
+    <url-pattern>*.html</url-pattern>
+  </servlet-mapping>
+  
+  <servlet-mapping>
+    <servlet-name>Template|XML</servlet-name>
+    <url-pattern>*.xhtml</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>Groovlet</servlet-name>
+    <url-pattern>*.groovy</url-pattern>
+  </servlet-mapping>
+
+  <welcome-file-list>
+    <welcome-file>index.xhtml</welcome-file>
+    <welcome-file>index.html</welcome-file>
+    <welcome-file>index.jsp</welcome-file>
+    <welcome-file>index.jspx</welcome-file>
+    <welcome-file>index.groovy</welcome-file>
+  </welcome-file-list>
+
+</web-app>
diff --git a/src/main/groovy/webapps/groovlet-examples/codehaus-style.css b/src/main/groovy/webapps/groovlet-examples/codehaus-style.css
new file mode 100644
index 0000000..86667ce
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/codehaus-style.css
@@ -0,0 +1,626 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+body {
+  font-family: Verdana, Helvetica, Arial, sans-serif;
+  font-size: small;
+  margin: 0;
+}
+
+div {
+  /* line-height: 1.5em; */
+}
+
+a {
+  color: #008800;
+  text-decoration: none;
+  font-weight: bold;
+}
+
+.navLink a {
+  font-weight: normal;
+}
+
+.navLink {
+  margin-left: 5px;
+}
+
+.navLink:first-line {
+  margin-left: -5px;
+}
+
+
+a:link.selfref, a:visited.selfref {
+}
+
+a:link, a:visited {
+}
+
+a:active, a:hover {
+  text-decoration: underline;
+}
+
+a.plain:active, a.plain:hover {
+  text-decoration: none;
+}
+
+.sectionTitle a {
+  text-decoration: underline;
+}
+.subsectionTitle a {
+  text-decoration: underline;
+}
+
+span.highlight {
+  font-weight: bold;
+  color: #990000;
+}
+
+#layout {
+  margin: 0px;
+  padding: 0px;
+}
+
+#banner {
+  padding: 8px;
+}
+
+#breadcrumbs {
+  border-top: 1px solid #009900;
+  border-bottom: 1px solid #009900;
+  padding-left: 12px;
+  padding-right: 12px;
+  padding-top: 2px;
+  padding-bottom: 2px;
+  font-size: x-small;
+  background-color: #dddddd;
+}
+
+#breadcrumbs td {
+  font-size: x-small;
+}
+
+#breadcrumbs a {
+  font-weight: bold;
+}
+
+#layout {
+  /*border-top: 1px solid #009900;*/
+  padding: 0px;
+  margin: 0px;
+
+}
+
+.navSection {
+  background-color: #ffffff;
+  border: 1px solid #999999;
+  border-top: none;
+  padding: 0px;
+  margin-bottom: 8px;
+  font-size: small;
+}
+
+.navSection a {
+  font-weight: normal;
+}
+
+.navSectionHead {
+  border-top: 1px solid #999999;
+  border-bottom: 1px solid #999999;
+  color: #555555;
+  padding: 4px;
+  margin-left: 0px;
+  margin-right: 0px;
+  background-color: #eeeeee;
+  font-weight: bold;
+  font-size: x-small;
+}
+
+.navLink {
+  padding-top: 2px;
+  padding-bottom: 2px;
+  padding-left: 14px;
+  font-size: small;
+}
+
+.section {
+  padding-bottom: 16px;
+}
+
+* + h1 {
+  margin-top: 1.5em;
+}
+
+* + h2 {
+  margin-top: 1.5em;
+}
+
+.sectionTitle, h1 {
+  padding: 4px;
+  border: 1px solid #aaaaaa;
+  color: #007700;
+  font-size: larger;
+  background-color: #eee;
+  color: #007700;
+  font-weight: bold;
+  margin-bottom: .5em;
+  /* margin-top: 1em; */
+}
+
+.subsection {
+  padding-left: 20px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+
+body:first-child {
+  padding-top: 0px;
+}
+
+.subsectionTitle, h2 {
+  padding-left: .2em;
+  /* margin-top: 1em; */
+  margin-bottom: .5em;
+  border-bottom: 1px solid #999;
+  border-left: 1px solid #999;
+  background-color: white;
+  font-weight: bold;
+  font-size: larger;
+}
+
+.sectionTitle a {
+  font-weight: normal;
+}
+
+.subsectionTitle a {
+  font-weight: normal;
+}
+
+.subsubsection {
+  padding-left: 30px;
+}
+
+.subsubsectionTitle, h3, .blogheading {
+  font-weight: bold;
+  border-bottom: 1px solid #bbb;
+  padding: .1em;
+  color: #555;
+  margin-bottom: .5em;
+}
+
+h4 {
+  margin-bottom: .5em;
+  padding: .1em;
+  border: 1px solid #ddd;
+
+}
+
+ins.inserted {
+  text-decoration: none;
+  font-weight: bold;
+}
+
+#leftColumn {
+  width: 12em;
+}
+
+#leftColumn h3 {
+  background-color: #ddd;
+  border: 1px solid #ccc;
+  border-bottom: 1px solid #070;
+  margin-bottom: .5em;
+}
+
+#leftColumn a {
+  font-weight: inherit;
+}
+
+#breadcrumbs a {
+  font-weight: inherit;
+}
+
+
+p {
+  margin: 0px;
+  margin-bottom: .5em;
+  line-height: 1.2em;
+  padding-right: 20px;
+  padding-left: 0px;
+}
+
+blockquote p {
+  padding-left: 0px;
+  padding-right: 0px;
+}
+
+ul {
+  margin-top: 1em;
+  margin-bottom: 1em;
+  padding-left: 1.5em;
+}
+
+ul ul {
+  margin: 0px;
+  padding-left: 1em;
+}
+
+li {
+  margin: 0px;
+  margin-right: 15%;
+  line-height: 1.2em;
+}
+
+#leftColumn {
+  border-right: 1px solid #cccccc;
+  background-color: #eeeeee;
+  padding: 12px;
+  font-size: small;
+}
+
+#leftColumn a {
+  font-size: smaller;
+}
+
+#leftColumn tt a {
+  font-size: inherit;
+}
+
+#leftColumn h3 a {
+  font-size: inherit;
+}
+
+tt {
+  font-size: larger;
+}
+
+
+#extraColumn {
+  padding: 12px;
+}
+
+#navBox {
+}
+
+#rightColumn {
+  padding: 12px;
+  border-right: 1px solid #cccccc;
+  font-size: small;
+  padding-left: 16px;
+  padding-right: 16px;
+}
+
+#contentBox {
+
+}
+
+table.bodyTable, table.wikitable {
+  margin: 10px;
+  border-collapse: collapse;
+  border-spacing: 0pt;
+  background-color: #eeeeee;
+}
+
+#Content table.grid {
+  border: 1px solid #bbbbbb;
+}
+
+table.grid {
+  padding: 0px;
+  border-collapse: collapse;
+  border-spacing: 0pt;
+  margin-left: 1em;
+  margin-right: 1em;
+}
+
+table.grid th {
+  background-color: #eeeeee;
+  font-size: smaller;
+  padding: 4px;
+  border: 1px solid #bbbbbb;
+}
+
+table.grid td {
+  font-size: x-small;
+  border: 1px solid #bbbbbb;
+  padding: 3px;
+}
+
+table.bodyTable th, table.bodyTable td, table.wikitable th, table.wikitable td {
+  border: 1px solid #999999;
+  font-size: smaller;
+  padding: 4px;
+}
+
+
+table.bodyTable th, table.wikitable th {
+  text-align: left;
+  background-color: #dddddd;
+  border: 2px solid #999999;
+  padding: 4px;
+}
+
+.nobr
+  white-space: nowrap;
+}
+
+table.bodyTable td {
+  padding: 4px;
+}
+
+/*
+table.bodyTable th, table.wikitable th {
+  border-bottom: 2px solid #999999;
+}
+
+table.bodyTable tr.a {
+  background-color: #dedede;
+}
+
+table.bodyTable tr.b {
+  background-color: #efefef;
+}
+*/
+
+.source, .code {
+  padding: 12px;
+  margin: 1em;
+  border: 1px solid #007700;
+  border-left: 2px solid #007700;
+  border-right: 2px solid #007700;
+  color: #555555;
+}
+
+pre {
+  padding: 12px;
+  font-size: larger;
+}
+
+.java-keyword {
+  color: #009900;
+}
+
+.java-object {
+  color: #000099;
+}
+
+.java-quote {
+  color: #990000;
+}
+
+
+.source, .code pre {
+  margin: 0px;
+  margin-left: 8px;
+  padding: 0px;
+}
+
+#footer {
+  padding-left: 4px;
+  border-top: 1px solid #009900;
+  color: #888888;
+  font-size: x-small;
+}
+
+blockquote {
+  border-top: 1px solid #bbbbbb;
+  border-bottom: 1px solid #bbbbbb;
+  border-left: 3px solid #bbbbbb;
+  border-right: 3px solid #bbbbbb;
+  padding: 12px;
+  margin-left: 3em;
+  margin-right: 3em;
+  color: #666666;
+  background-color: white;
+  line-height: 1.5em;
+}
+
+input[type="text"] {
+  margin: 0px;
+  border: 1px solid #999999;
+  background-color: #dddddd;
+}
+
+input.required {
+  margin: 0px;
+  border: 1px solid #990000;
+}
+
+input {
+  border: 1px solid #999999;
+}
+
+textarea {
+  border: 1px solid #999999;
+}
+
+textarea.required {
+  border: 1px solid #990000;
+}
+
+label {
+  font-size: smaller;
+}
+
+label.required {
+  color: #990000;
+}
+
+.searchResults {
+  color: black;
+}
+
+.searchResults b {
+  color: #007700;
+}
+
+
+.linecomment { color: #bbbbbbb; }
+.blockcomment { color: #bbbbbbb; }
+.prepro { color: #0000BB; }
+.select {}
+.quote { color: #770000; }
+.category1 { color: #007700; }
+.category2 { color: #0000BB; }
+.category3 { color: #0000BB; }
+
+#page_title {
+  border-bottom: 1px solid black;
+  font-weight: bold;
+  font-size: x-large;
+  margin-bottom: .5em;
+}
+
+.greenbar {
+  background-color: green;
+}
+
+.redbar {
+  background-color: red;
+}
+
+tr.testpassed td {
+  padding: 0px;
+  padding-left: 1px;
+  padding-right: 1px;
+  margin: 0px;
+}
+
+tr td.noformatting {
+  border: none;
+  padding: 0px;
+  padding-left: 4px;
+  padding-right: 4px;
+  margin: 0px;
+}
+
+.greybox {
+  font-style: italic;
+  font-weight: bold;
+  margin-top: .5em;
+  margin-bottom: .5em;
+  background-color: #ddd;
+  border: 1px solid #bbb;
+  padding: .3em;
+}
+
+.panelContent {
+  border: 1px solid #999;
+  padding: 1em;
+  margin: 1em;
+}
+
+.header_name {
+	font-size: smaller;
+	padding: 2px;
+	padding-right: 1ex;
+	border-right: 1px solid #555;
+	background-color: #ccc;
+}
+
+.header_value {
+	font-size: smaller;
+	background-color: #ddd;
+	padding: 2px;
+}
+
+.header_fields {
+	width: 100%;
+	border: 1px solid #999;
+	background-color: #fff;
+}
+
+.email_body {
+	margin: 2ex;
+	padding: 1ex;
+	padding-left: 2ex;
+	padding-right: 2ex;
+	border: 1px solid #999;
+	font-size: smaller;
+}
+
+.email_body pre {
+	padding: 0px;
+	margin: 0px;
+}
+
+.email_body blockquote {
+	padding: 0px;
+	margin: 0px;
+	border: 1px solid #ccc;
+}
+
+.msg_navblock {
+	margin-bottom: 2ex;
+	border: 1px solid #999;
+	background-color: #fff;
+}
+
+.msg_navblock th {
+	border: 1px solid #ccc;
+	font-size: smaller;
+}
+
+.msg_navblock td {
+	border: 1px solid #ccc;
+	font-size: smaller;
+}
+
+.single_entry {
+	border: 1px solid #aaa;
+	padding: .5ex;
+	padding-left: 1ex;
+	border-left: 1px solid #090;
+	margin: 2px;
+	background-color: #eee;
+}
+
+.root_entry {
+	border: 1px solid #aaa;
+	padding: .5ex;
+	padding-left: 1ex;
+	border-left: 1px solid #090;
+	margin-top: 8px;
+	margin-bottom: 8px;
+	background-color: #eee;
+}
+
+.root_entry .sub_entry {
+	padding: .5ex;
+	padding-left: 1ex;
+	margin: 2px;
+	margin-left: 1ex;
+	border-left: 1px solid #999;
+}
+
+.list_entry {
+	border: 1px solid #aaa;
+	padding: 1ex;
+	margin-top: 1ex;
+	margin-bottom: 1ex;
+}
+
+.list_entry td {
+	border: 1px solid #999;
+}
+
+.project_entry {
+	border: 1px solid #aaa;
+	padding: 1ex;
+	margin-top: 1ex;
+	margin-bottom: 1ex;
+}
diff --git a/src/main/groovy/webapps/groovlet-examples/hello/hello.groovy b/src/main/groovy/webapps/groovlet-examples/hello/hello.groovy
new file mode 100644
index 0000000..85b8617
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/hello/hello.groovy
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - hello</title>
+    </head>
+    <body>
+    <a href="../"><img src="../images/return.gif" width="24" height="24" border="0"></a><a href="../">Return</a>
+    <p>
+"""
+
+session = request.getSession(true);
+
+if (session.counter == null) {
+  session.counter = 1
+}
+
+
+println """Hello, ${request.remoteHost}! ${new java.util.Date()}"""
+
+println """
+<dl>
+ <dt><b>requestURI</b></dt><dd>${request.requestURI}</dd>
+ <dt><b>servletPath</b></dt><dd>${request.servletPath}</dd>
+ <dt><b>session.counter</b></dt><dd>${session.counter}</dd>
+</dl>
+"""
+
+println """
+    </body>
+</html>
+"""
+
+session.counter = session.counter + 1
diff --git a/src/main/groovy/webapps/groovlet-examples/images/code.gif b/src/main/groovy/webapps/groovlet-examples/images/code.gif
new file mode 100644
index 0000000..93af2cd
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/images/code.gif
Binary files differ
diff --git a/src/main/groovy/webapps/groovlet-examples/images/execute.gif b/src/main/groovy/webapps/groovlet-examples/images/execute.gif
new file mode 100644
index 0000000..f64d70f
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/images/execute.gif
Binary files differ
diff --git a/src/main/groovy/webapps/groovlet-examples/images/groovy.png b/src/main/groovy/webapps/groovlet-examples/images/groovy.png
new file mode 100644
index 0000000..54af4c1
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/images/groovy.png
Binary files differ
diff --git a/src/main/groovy/webapps/groovlet-examples/images/return.gif b/src/main/groovy/webapps/groovlet-examples/images/return.gif
new file mode 100644
index 0000000..af4f68f
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/images/return.gif
Binary files differ
diff --git a/src/main/groovy/webapps/groovlet-examples/index.groovy b/src/main/groovy/webapps/groovlet-examples/index.groovy
new file mode 100644
index 0000000..6a62dc6
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/index.groovy
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+println """
+
+<!-- Groovy Groovlet Examples. -->
+
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="Groovy Developers">
+   <title>Groovy - Groovlet Examples</title>
+   <link rel="stylesheet" href="codehaus-style.css" type="text/css">
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a href="http://groovy.codehaus.org"><img src="images/groovy.png" border="0"></a>
+
+<h1>Groovlet examples showing GroovyServlet in action</h1>
+<p>
+These examples will only work when viewed via an http URL. They will not work
+if you are viewing these pages via a <tt>file://...</tt> URL. You need to
+deploy the <tt>war</tt> archive into a servlet container like Tomcat, Jetty or
+any other will do as well.
+</p>
+
+<p>
+To navigate your way through the examples, the following icons will help:
+
+<table border=0>
+ <tr>
+  <td width="30"><img src="images/execute.gif" ></td>
+  <td>Executes the example</td>
+</tr>
+<tr>
+  <td width="30"><img src="images/code.gif"></td>
+  <td>Look at the source code for the example</td>
+</tr>
+<tr>
+  <td width="30"><img src="images/return.gif"></td>
+  <td>Return to this screen</td>
+</tr>
+</table>
+</p>
+
+<p>Tip: To see the cookie interactions with your browser, try turning on
+the "notify when setting a cookie" option in your browser preferences.
+This will let you see when a session is created and give some feedback
+when looking at the cookie demo.
+</p>
+
+<h2>Table of content</h2>
+
+"""
+
+println """
+
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+
+<!-- Begin Groovlet -->
+<tr VALIGN=TOP>
+<td>Hello World</td>
+
+<td VALIGN=TOP WIDTH="30%"><img SRC="images/execute.gif" HSPACE=4 BORDER=0 align=TOP><a href="hello/hello.groovy">Execute</a></td>
+
+<td WIDTH="30%"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP>Source</td>
+</tr>
+<!-- End Groovlet -->
+
+
+<!-- Begin Groovlet -->
+<tr VALIGN=TOP>
+<td>Zoo</td>
+
+<td VALIGN=TOP WIDTH="30%"><img SRC="images/execute.gif" HSPACE=4 BORDER=0 align=TOP><a href="zoo/zoo.groovy">Execute</a></td>
+
+<td WIDTH="30%"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP>Source</td>
+</tr>
+<!-- End Groovlet -->
+</table>
+
+<h2>Note</h2>
+
+<p>The source code for these examples does not contain all of the
+source code that is actually in the example, only the important sections
+of code. Code not important to understand the example has been removed
+for clarity.
+</body>
+</html>
+"""
\ No newline at end of file
diff --git a/src/main/groovy/webapps/groovlet-examples/readme.txt b/src/main/groovy/webapps/groovlet-examples/readme.txt
new file mode 100644
index 0000000..bd57e05
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/readme.txt
@@ -0,0 +1,32 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+
+GroovyServlet examples
+
+[ / ]
+Only contains the "index.groovy", "codehaus-style.css" and "readme.txt" files.
+
+[ /hello ]
+Contains a single welcome file named "hello.groovy" showing a simple groovlet.
+
+[ /zoo ]
+The zoo demonstrates a groovy setup of scripts, that are partly <i>hidden</i>
+beneath the "WEB-INF/groovy" directory. Note the different package names in the
+script files.
diff --git a/src/main/groovy/webapps/groovlet-examples/xml/index.xhtml b/src/main/groovy/webapps/groovlet-examples/xml/index.xhtml
new file mode 100644
index 0000000..06c9a17
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/xml/index.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<!-- DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" -->
+
+<html xmlns:gsp="http://groovy.codehaus.org/2005/gsp">
+
+  <head>
+    <link rel="stylesheet" href="../codehaus-style.css" type="text/css" />
+  </head>
+
+  <body>
+
+  Name = ${request.getParameter("name")} <br/>
+  Name = <gsp:expression>params.get("name")</gsp:expression>
+
+  <hr color="#12334" noshade="noshade" />
+
+  BEGIN
+
+    <br/>
+
+  <gsp:scriptlet>
+   <![CDATA[
+    3.times {
+   ]]>
+  </gsp:scriptlet>
+* Hello World! *
+  <gsp:scriptlet>
+   <![CDATA[  } // 3.times ]]>
+  </gsp:scriptlet>  
+
+    <br/>
+
+    END
+
+  </body>
+
+</html>
diff --git a/src/main/groovy/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy b/src/main/groovy/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy
new file mode 100644
index 0000000..2280e05
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package zoo
+
+class HommingbergerGepardenforelle extends zoo.fish.Trout {
+
+  String saySomething(String something) {
+    return something;
+  }
+  
+}
\ No newline at end of file
diff --git a/src/main/groovy/webapps/groovlet-examples/zoo/visit.groovy b/src/main/groovy/webapps/groovlet-examples/zoo/visit.groovy
new file mode 100644
index 0000000..7c81126
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/zoo/visit.groovy
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+Animal shark = new zoo.fish.Shark()
+Animal trout = new zoo.fish.Trout()
+Animal forelle = new zoo.HommingbergerGepardenforelle()
+
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - Visiting the zoo</title>
+    </head>
+    <body>
+     <p>Shark<br> 
+     ${shark.saySomething("\"Where is the trout?\"")}
+     <p>Trout<br> 
+     ${trout.saySomething("Here is the trout!")}
+     <p>Forelle<br> 
+     ${forelle.saySomething("\"<a href=\"http://www.hommingberger-forelle.de\">There is no spoon.</a>\"")}
+    </body>
+</html>
+"""
\ No newline at end of file
diff --git a/src/main/groovy/webapps/groovlet-examples/zoo/zoo.groovy b/src/main/groovy/webapps/groovlet-examples/zoo/zoo.groovy
new file mode 100644
index 0000000..38a4c94
--- /dev/null
+++ b/src/main/groovy/webapps/groovlet-examples/zoo/zoo.groovy
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - Visiting the zoo</title>
+    </head>
+    <body>
+    <a href="../"><img src="../images/return.gif" width="24" height="24" border="0"></a><a href="../">Return</a>
+    <p>
+"""
+
+Animal shark = new zoo.fish.Shark()
+Animal trout = new zoo.fish.Trout()
+Animal forelle = new zoo.HommingbergerGepardenforelle()
+
+println """
+     <p>Shark<br>
+     ${shark.saySomething("\"Where is the trout?\"")}
+
+     <p>Trout<br>
+     ${trout.saySomething("Here is the trout!")}
+
+     <p>Forelle<br>
+     ${forelle.saySomething("\"<a href=\"http://www.hommingberger-forelle.de\">There is no spoon.</a>\"")}
+     <!-- http://en.wikipedia.org/wiki/Nigritude_ultramarine -->
+"""
+
+println """
+    </body>
+</html>
+"""
diff --git a/src/main/groovy/webapps/gsp-examples/readme.txt b/src/main/groovy/webapps/gsp-examples/readme.txt
new file mode 100644
index 0000000..6f197c6
--- /dev/null
+++ b/src/main/groovy/webapps/gsp-examples/readme.txt
@@ -0,0 +1,20 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+TODO Second, setup WEB-INF and examples gsp files.
\ No newline at end of file
diff --git a/src/main/groovy/webapps/template-examples/3.times.HelloWorld.html b/src/main/groovy/webapps/template-examples/3.times.HelloWorld.html
new file mode 100644
index 0000000..d270530
--- /dev/null
+++ b/src/main/groovy/webapps/template-examples/3.times.HelloWorld.html
@@ -0,0 +1,33 @@
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<html>
+  <body>
+
+    <% 3.times { %>
+      Hello World!
+    <% } %>
+
+    <br>
+
+    session id = ${session.id}
+
+  </body>
+</html>
diff --git a/src/main/groovy/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder b/src/main/groovy/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder
new file mode 100644
index 0000000..f0f8ae5
--- /dev/null
+++ b/src/main/groovy/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder
@@ -0,0 +1,18 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
\ No newline at end of file
diff --git a/src/main/groovy/webapps/template-examples/WEB-INF/web.xml b/src/main/groovy/webapps/template-examples/WEB-INF/web.xml
new file mode 100644
index 0000000..2345235
--- /dev/null
+++ b/src/main/groovy/webapps/template-examples/WEB-INF/web.xml
@@ -0,0 +1,46 @@
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+<servlet>
+    <servlet-name>template</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.htm</url-pattern>
+  </servlet-mapping>
+  
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.html</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.template</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/src/main/groovy/webapps/template-examples/javasystemproperties.htm b/src/main/groovy/webapps/template-examples/javasystemproperties.htm
new file mode 100644
index 0000000..fa1f2c5
--- /dev/null
+++ b/src/main/groovy/webapps/template-examples/javasystemproperties.htm
@@ -0,0 +1,42 @@
+<!--
+
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+
+-->
+<html>
+<head>
+<title>System properties</title>
+</head>
+<body>
+
+<h1>System properties</h1>
+<% keys = System.getProperties().keySet();
+   if (keys.isEmpty()) {
+     out.println('No keys.');
+   }
+   else {
+  %>
+<dl>
+    <% for (key in keys) { %>
+    <dt> <b> ${key} </b> </dt> 
+    <dd> <tt> ${System.getProperty(key)} </tt> </dd>
+    <% } %>
+</dl>
+<% } %>
+</body>
+</html>
diff --git a/src/main/groovy/webapps/template-examples/readme.txt b/src/main/groovy/webapps/template-examples/readme.txt
new file mode 100644
index 0000000..269ffbd
--- /dev/null
+++ b/src/main/groovy/webapps/template-examples/readme.txt
@@ -0,0 +1,32 @@
+====
+     Licensed to the Apache Software Foundation (ASF) under one
+     or more contributor license agreements.  See the NOTICE file
+     distributed with this work for additional information
+     regarding copyright ownership.  The ASF licenses this file
+     to you under the Apache License, Version 2.0 (the
+     "License"); you may not use this file except in compliance
+     with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing,
+     software distributed under the License is distributed on an
+     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+     specific language governing permissions and limitations
+     under the License.
+====
+
+
+TemplateServlet examples
+
+[ /readme.txt ]
+This readme file. For clarity sake, it does not contain a welcome file and
+therefore the servlet container should list all files and subdirectories listed
+below.
+
+[ /3.times.HelloWorld.html ]
+Prints three times "Hello World!" and the session id.
+
+[ /javasystemproperties.htm ]
+Lists Java runtime system properties.