Last updated by 5 years ago

Page: Developer - Artefact API, Version:0

GrailsApplication's Artefact API

Since Grails 0.5 only. This document and the codebase are a work in progress

Prior to version 0.5 new artefacts had to be hardwired into code in GrailsApplication and DefaultGrailsApplication. For example the addition of the Codecs via a plugin in Grails 0.4 required new addCodecClass(xx) methods etc to be added to core Grails classes.

Detailed rationale for the change:

  • that neither GCU nor GrailsApplication should know about generic artefacts (there are a couple of exceptions currently like getGrailsDataSource()).
  • only internal code and maybe some existing plugins are using these methods
  • the method naming has been inconsistent
  • there were many duplicate methods with differing names
  • I have made DefaultGrailsApplication implement GroovyObjectSupport and used that to allow groovy code to access synthetic properties and methods to make accessing artefact data much like it was previously.
  • deprecation does not produce warnings to groovy code (it is unlikely there is much if any Java code outside Grails calling these methods)
  • we are not 1.0
Now we have a generic API which is partly implemented. Most dependencies on direct artefact knowledge are removed and replaced with the new API on GrailsApplication:

/**
     * <p>Call this to find out if the class you have is an artefact loaded by grails.</p>
     * @param theClazz A class to test
     * @return True if and only if the class was loaded from grails-app/
     * @since 0.5
     */
    public boolean isArtefact(Class theClazz);

/** * <p>Check if the specified artefact Class has been loaded by Grails already AND is * of the type expected</p> * @param artefactType A string identifying the artefact type to check for * @param theClazz The class to check * @return True if Grails considers the class to be managed as an artefact of the type specified. * @since 0.5 */ public boolean isArtefactOfType(String artefactType, Class theClazz);

/** * <p>Check if the artefact Class with the name specified is of the type expected</p> * @param artefactType A string identifying the artefact type to check for * @param className The name of a class to check * @return True if Grails considers the class to be managed as an artefact of the type specified. * @since 0.5 */ public boolean isArtefactOfType(String artefactType, String className);

/** * <p>Gets the GrailsClass associated with the named artefact class</p> * <p>i.e. to get the GrailsClass for controller called "BookController" you pass the name "BookController"</p> * @param artefactType The type of artefact to retrieve, i.e. "Controller" * @param name The name of an artefact such as "BookController" * @return The associated GrailsClass or null * @since 0.5 */ public GrailsClass getArtefact(String artefactType, String name);

/** * <p>Obtain all the class information about the artefactType specified</p> * @param artefactType An artefact type identifier i.e. "Domain" * @return The artefact info or null if the artefactType is not recognized * @since 0.5 */ public ArtefactInfo getArtefactInfo(String artefactType);

/** * <p>Get an array of all the GrailsClass instances relating to artefacts of the specified type.</p> * @param artefactType The type of artefact to retrieve, i.e. "Task" * @return An array of GrailsClasses which may empty by not null * @since 0.5 */ public GrailsClass[] getArtefacts(String artefactType);

/** * <p>Get an artefact GrailsClass by a "feature" which depending on the artefact may be a URI or tag name * for example</p> * @param artefactType The type ID of the artefact, i.e. "TagLib" * @param featureID The "feature" ID, say a URL or tag name * @return The grails class or null if none is found * @since 0.5 */ public GrailsClass getArtefactForFeature(String artefactType, Object featureID);

/** * <p>Registers a new artefact</p> * @param artefactType The type ID of the artefact, i.e. "TagLib" * @param artefactClass The class of the artefact. A new GrailsClass will be created automatically and added * to internal structures, using the appropriate ArtefactHandler * @return The new grails class for the artefact class * @since 0.5 */ public GrailsClass addArtefact(String artefactType, Class artefactClass);

/** * <p>Registers a new artefact</p> * @param artefactType The type ID of the artefact, i.e. "TagLib" * @param artefactGrailsClass The GrailsClass of the artefact. * @return The supplied grails class for the artefact class * @since 0.5 */ public GrailsClass addArtefact(String artefactType, GrailsClass artefactGrailsClass);

/** * <p>Register a new artefact handler</p> * @param handler The new handler to add */ public void registerArtefactHandler(ArtefactHandler handler);

The new interfacce ArtefactHandler allows us to register an object that provides the knowledge about how to deal with a given artefact. This includes detecting whether any Class is indeed following the artefact's conventions, wrapping it in a GrailsClass so it can be managed, and performing any special initialization (i.e. dependency checks).

Therefore you will no see a lot of code of the form:

if (application.getArtefact(DomainClassArtefactHandler.TYPE, domainClassName))
...

Dynamic methods

To make up for the ugliness of having to pass in the type all the type from groovy code, DefaultGrailsApplication implements GroovyObjectSupport to add some dynamic methods and properties accessible only from Groovy code:

application.domainClasses (a usage of dynamic property application.<artefactTypeIDHere>Classes) application.getDomainClasses (same as above, method form) application.isDomainClass(class) (dynamic method application.is<ArtefactTypeIDHere>Class(classorname)) application.getDomainClass(name) (dynamic method application.get<ArtefactTypeIDHere>Class(classorname))

This is used extensively throughout Grails core plugins and taglibs now.

Registering custom artefacts

Plugins can easily provide their own artefacts. All you need to do is create an ArtefactHandler implementation and register it in your main plugin class:

class MyGrailsPlugin {
    def artefacts = [ org.somewhere.MyArtefactHandler ]
    …
}

The 'artefacts' list can contain either handler classes or instances of handlers. So, what does an artefact handler look like? Well, put simply it is an implementation of the ArtefactHandler interface. To make life a bit easier, there is a skeleton implementation that can readily be extended: ArtefactHandlerAdapter.

In addition to the handler itself, every new artefact needs a corresponding wrapper class that implements GrailsClass. The best way to understand how both the handler and wrapper classes work is to look at the Quartz plugin:

GrailsTaskClass DefaultGrailsTaskClass TaskArtefactHandler