//----------------------------------------------------------------------------------
// Groovy adopts many of the Java structuring conventions and terminology
// and adds some concepts of its own.
// Code-reuse can occur at the script, class, library, component or framework level.
// Source code including class file source and scripts are organised into packages.
// These can be thought of as like hierarchical folders or directories. Two class
// with the same name can be distinguished by having different packages. Compiled
// byte code and sometimes source code including scripts can be packaged up into
// jar files. Various conventions exist for packaging classes and resources in
// such a way to allow them to be easily reused. Some of these conventions allow
// reusable code to be placed within repositories for easy use by third parties.
// One such repository is the maven repository, e.g.: ibiblio.org/maven2
// When reusing classes, it is possible to compartmentalise knowledge of
// particular packages using multiple (potentially hierarchical) classloaders.
// By convention, package names are all lowercase. Class names are capitalized.
// Naming examples:
// package my.package1.name // at most one per source file - at top of file
// class MyClass ... // actually defines my.package1.name.MyClass
// import my.package1.name.MyClass // allows package to be dropped within current file
// import my.package2.name.MyClass // if class basenames are the same, can't
// // import both, leave one fully qualified
// import my.package.name.* // all classes in package can drop package prefix
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // No equivalent export process exists for Groovy. // If you have some Groovy functionality that you would like others to use // you either make the source code available or compile it into class files // and package those up in a jar file. Some subset of your class files will // define the OO interface to your functionality, e.g. public methods, // interfaces, etc. Depending on the circumstances, various conventions are // used to indicate this functionality including Manifest files, javadocs, // deployment descriptors, project metadata and dependency management files. // See 12.18 for an example. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Groovy supports both static and dynamic (strong) typing. When trying to // compile or run files using static typing, the required classes referenced // must be available. Classes used in more dynamic ways may be loaded (or // created) at runtime. Errors in loading such dynamic cases are handled // using the normal exception handling mechanisms. // attempt to load an unknown resource or script: try { evaluate(new File('doesnotexist.groovy')) } catch (Exception FileNotFoundException) { println 'File not found, skipping ...' } // => File not found, skipping ... // attempt to load an unknown class: try { Class.forName('org.happytimes.LottoNumberGenerator') } catch (ClassNotFoundException ex) { println 'Class not found, skipping ...' } // -> Class not found, skipping ... // dynamicallly look for a database driver (slight variation to original cookbook) // Note: this hypothetical example ignores certain issues e.g. different url // formats for configuration when establishing a connection with the driver candidates = [ 'oracle.jdbc.OracleDriver', 'com.ibm.db2.jcc.DB2Driver', 'com.microsoft.jdbc.sqlserver.SQLServerDriver', 'net.sourceforge.jtds.jdbc.Driver', 'com.sybase.jdbc3.jdbc.SybDriver', 'com.informix.jdbc.IfxDriver', 'com.mysql.jdbc.Driver', 'org.postgresql.Driver', 'com.sap.dbtech.jdbc.DriverSapDB', 'org.hsqldb.jdbcDriver', 'com.pointbase.jdbc.jdbcUniversalDriver', 'org.apache.derby.jdbc.ClientDriver', 'com.mckoi.JDBCDriver', 'org.firebirdsql.jdbc.FBDriver', 'sun.jdbc.odbc.JdbcOdbcDriver' ] loaded = null for (driver in candidates) { try { loaded = Class.forName(driver).newInstance() break } catch (Exception ex) { /* ignore */ } } println loaded?.class?.name // => sun.jdbc.odbc.JdbcOdbcDriver //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // In Groovy (like Java), any static reference to an external class within // your class will cause the external class to be loaded from the classpath. // You can dynamically add to the classpath using: // this.class.rootLoader.addURL(url) // To delay loading of external classes, use Class.forName() or evaluate() // the script separately as shown in 12.2. // For the specific case of initialization code, here is another example: // (The code within the anonymous { ... } block is called whenever the // class is loaded.) class DbHelper { def driver { if (System.properties.'driver' == 'oracle') driver = Class.forName('oracle.jdbc.OracleDriver') else driver = Class.forName('sun.jdbc.odbc.JdbcOdbcDriver') } } println new DbHelper().driver.name // => sun.jdbc.odbc.JdbcOdbcDriver // call program with -Ddriver=oracle to swap to other driver // A slightly related feature: If you want to load a script (typically in a // server environment) whenever the source file changes, use GroovyScriptEngine() // instead of GroovyShell() when embedding groovy. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // class variables are private unless access functions are defined class Alpha { def x = 10 private y = 12 } println new Alpha().x // => 10 println new Alpha().y // => 12 when referenced inside source file, error outside //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // You can examine the stacktrace to determine the calling class: see 10.4 // When executing a script from a groovy source file, you can either: println getClass().classLoader.resourceLoader.loadGroovySource(getClass().name) // => file:/C:/Projects/GroovyExamples/Pleac/classes/pleac12.groovy // or for the initially started script when started using the standard .bat/.sh files println System.properties.'script.name' //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// For code which executes at class startup, see the initialization code block
// mechanism mentioned in 12.3. For code which should execute during shutdown
// see the finalize() method discussed (including limitations) in 13.2.
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// Each JVM process may have its own classpath (and indeed its own version of Java
// runtime and libraries). You "simply" supply a classpath pointing to different
// locations to obtain different modules.
// Groovy augments the JVM behaviour by allowing individuals to have a ~/.groovy/lib
// directory with additional libraries (and potentially other resources).
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// To make your code available to others could involve any of the following:
// (1) make your source code available
// (2) if you are creating a standard class, use the jar tool to package the
// compiled code into a jar - this is then added to the classpath to use
// (3) if the jar relies on additional jars, this is sometimes specified in
// a special manifest file within the jar
// (4) if the code is designed to run within a container environment, there
// might be additional packaging, e.g. servlets might be packaged in a war
// file - essentially a jar file with extra metadata in xml format.
// (5) you might also supply your package to a well known repository such as the
// maven repository - and you will add dependency information in xml format
// (6) you may use platform specific installers to produce easily installable
// components (e.g. windows .exe files or linux rpm's)
// (7) you may spackage up your components as a plugin (e.g. as an eclipse plugin)
// this is also typically in jar/zip like format with additional metadata
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// Groovy has no SelfLoader. Class loading can be delayed using external scripts
// and by using the Class.forName() approach discussed in 12.2/12.3. If you have
// critical performance issues, you can use these techniques and keep your class
// size small to maximise the ability to defer loading. There are other kinds of
// performance tradeoffs you can make too. Alot of work has been done with JIT
// (just in time) compilers for bytecode. You can pre-compile Groovy source files
// into bytecode using the groovy compiler (groovyc). You can also do this on
// the fly for scripts you know you are going to need shortly.
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// Groovy has no AutoLoader. See the discussion in 12.9 for some techniques to
// impact program performance. There are many techniques available to speed up
// program performance (and in particular load speed). Some of these utilise
// techniques similar in nature to the technique used by the AutoLoader.
// As already mentioned, when you load a class into the JVM, any statically
// referenced class is also loaded. If you reference interfaces rather than
// concrete implementations, only the interface need be loaded. If you must
// reference a concrete implementation you can use either a Proxy class or
// classloader tricks to delay the loading of a full class (e.g. you supply a
// Proxy class with just one method implemented or a lazy-loading Proxy which
// loads the real class only when absolutely required)
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // You can use Categories to override Groovy and Java base functionality. println new Date().time // => 1169019557140 class DateCategory { // the class name by convention ends with category // we can add new functionality static float getFloatTime(Date self) { return (float) self.getTime() } // we can override existing functionality (now seconds since 1970 not millis) static long asSeconds(Date self) { return (long) (self.getTime()/1000) } } use (DateCategory) { println new Date().floatTime // => 1.1690195E12 println new Date().asSeconds() // => 1169019557 } // We can also use the 'as' keyword class MathLib { def triple(n) { n * 4 } def twice(n) { n * 2 } } def m = new MathLib() println m.twice(10) // => 20 println m.triple(10) // => 40 (Intentional Bug!) // we might want to make use of some funtionality in the math // library but want to later some of its features slightly or fix // some bugs, we can simply import the original using a different name import MathLib as BuggyMathLib // now we could define our own MathLib which extended or had a delegate // of the BuggyMathLib class //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// Many Java and Groovy programs emit a stacktrace when an error occurs.
// This shows both the calling and called programs (with line numbers if
// supplied). Groovy pretties up stacktraces to show less noise. You can use -d
// or --debug on the commandline to force it to always produce full stacktraces.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // already have log10, how to create log11 to log100 (11..100).each { int base -> binding."log$base" = { int n -> Math.log(n) / Math.log(base) } } println log20(400) // => 2.0 println log100(1000000) // => 3.0 (displays 2.9999999999999996 using doubles) // same thing again use currying def logAnyBase = { base, n -> Math.log(n) / Math.log(base) } (11..100).each { int base -> binding."log$base" = logAnyBase.curry(base) } println log20(400) // => 2.0 println log100(1000000) // => 3.0 (displays 2.9999999999999996 using doubles) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Groovy intefaces with C in the same way as Java: using JNI // For this discussion we will ignoring platform specific options and CORBA. // This tutorial here describes how to allow Java (and hence Groovy) to // call a C program which generates UUIDs: // http://ringlord.com/publications/jni-howto/ // Here's another useful reference: // http://weblogs.java.net/blog/kellyohair/archive/2006/01/compilation_of_1.html // And of course, Sun's tutorial: // http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html // You might also want to consider SWIG which simplifies connecting // C/C++ to many scripting languages including Java (and hence Groovy) // More details: http://www.swig.org/ //---------------------------------------------------------------------------------- |
//----------------------------------------------------------------------------------
// See discussion for 12.14
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
// The standard documentation system for Java is JavaDoc.
// Documentation for JavaDoc is part of a Java installation.
// Groovy has a GroovyDoc tool planned which expands upon the JavaDoc tool
// but work on the tool hasn't progressed much as yet.
//----------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------- // Most libraries for Java (and hence Groovy) come precompiled. You simply download // the jar and place it somewhere on your CLASSPATH. // If only source code is available, you need to download the source and follow any // instuctions which came with the source. Most projects use one of a handful of // build tools to compile, test and package their artifacts. Typical ones are Ant // and Maven which you need to install according to their respective instructions. // If using Ant, you need to unpack the source files then type 'ant'. // If using Maven, you need to unpack the source files then type 'maven'. // If you are using Maven or Ivy for dependency management you can add // the following lines to your project description file. /* <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2</version> </dependency> */ // This will automatically download the particular version of the referenced // library file and also provide hooks so that you can make this automatically // available in your classpath. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // example groovy file for a "module" import org.apache.commons.lang.WordUtils class Greeter { def name Greeter(who) { name = WordUtils.capitalize(who) } def salute() { "Hello $name!" } } // test class class GreeterTest extends GroovyTestCase { def testGreeting() { assert new Greeter('world').salute() } } // Typical Ant build file (could be in Groovy instead of XML): /* <?xml version="1.0"?> <project name="sample" default="jar" basedir="."> <property name="src" value="src"/> <property name="build" value="build"/> <target name="init"> <mkdir dir="${build}"/> </target> <target name="compile" depends="init"> <mkdir dir="${build}/classes"/> <groovyc srcdir="${src}" destdir="${build}/classes"/> </target> <target name="test" depends="compile"> <groovy src="${src}/GreeterTest.groovy"> </target> <target name="jar" depends="compile,test"> <mkdir dir="${build}/jar"/> <jar destfile="${build}/jar/Greeter.jar" basedir="${build}/classes"> <manifest> <attribute name="Main-Class" value="Greeter"/> </manifest> </jar> </target> </project> */ // Typical dependency management file /* <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>groovy</groupId> <artifactId>module</artifactId> <name>Greeter</name> <version>1.0</version> <packaging>jar</packaging> <description>Greeter Module/description> <dependencies> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.2</version> </dependency> </dependencies> </project> */ //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Searching available modules in repositories: // You can browse the repositories online, e.g. ibiblio.org/maven2 or various // plugins are available for IDEs which do this for you, e.g. JarJuggler for IntelliJ. // Searching currently "installed" modules: // Browse your install directory, view your maven POM file, look in your ~/.groovy/lib // directory, turn on debug modes and watch classloader messages ... //---------------------------------------------------------------------------------- |