Monday, February 28, 2011

How to support custom artifact type in Griffon 2nd revision

Last year June I wrote a blog post about how to support custom artifact type in Griffon framework. Now Griffon 0.9.2 release is just around the corner. In this release some major refactoring was done with the Artifact API, that's why I am updating this guide to match the new API.

Out-of-box Griffon supports 4 different types of artifacts: Model, View, Controller (MVC) plus Service, these are the major building blocks of any Griffon application. Just like it's cousin Grails, in Griffon plugins and addons can also introduce new artifact types, however the process is fairly different from what Grails employs. As part of Griffon Validation Plugin I implemented the support for a new custom artifact type - Constraint, and I would like to share some of my learning here so it would be a little bit easier if you are planning to do something similar.

Step 1 - Handle your artifact

To support a new artifact type you have to tell the Griffon core about the artifact type first. You can achieve this by implementing your own ArtifactClass and ArtifactHandler, for most of the common cases extending from DefaultGriffonClass and ArtifactHandlerAdapter should be enough. Here is what it looks like for the Constraint artifact type:

public interface GriffonConstraintClass extends GriffonClass {
/** "constraint" */
String TYPE = "constraint";
/** "Constraint" */
String TRAILING = "Constraint";
}

public class ConstraintArtifactHandler extends ArtifactHandlerAdapter {
public ConstraintArtifactHandler(GriffonApplication app) {
super(app, GriffonConstraintClass.TYPE, GriffonConstraintClass.TRAILING);
}

protected GriffonClass newGriffonClassInstance(Class clazz) {
return new DefaultGriffonConstraintClass(getApp(), clazz);
}
}

public class DefaultGriffonConstraintClass extends DefaultGriffonClass implements GriffonConstraintClass {
public DefaultGriffonConstraintClass(GriffonApplication app, Class clazz) {
super(app, clazz, GriffonConstraintClass.TYPE, GriffonConstraintClass.TRAILING);
}
}
One note of caution on the artifact class and it's handler:
I ran into some problem when implementing them in Groovy initially, and had to change all implementation to Java instead. Thanks to Andres for the investigation and input on this discovery.

Step 2 - Register your artifact

As shown above, if you have experience working with Grails artefact support, you will notice the handler implementation is almost identical in Griffon, however things starting to differ from this point forward. Now you have the handler implemented, next thing is to register it with Griffon core. This is best achieved during initialization phase in your addon. Open the [PluginName]GriffonAddon.groovy file add the following callback if its not already there:


def addonInit = {app ->
....
app.artifactManager.registerArtifactHandler(new ConstraintArtifactHandler(app))
....
}


Step 3 - Find your artifacts

Now we have the new artifact type registered, next step is to tell Griffon where to find the artifacts. Ever wonder how Griffon knows to look under griffon-app/models for model classes? This is what we are going to do in this step. This is also where Griffon custom artifact support truly differs from Grails. Instead of handling it at run-time, Griffon chooses to handle this at build time, so to achieve this you need to tap into the Griffon event model. Open the _Events.groovy script and implement the following event listener:


eventCollectArtifacts = { artifactsInfo ->
if(!artifactsInfo.find{ it.type == 'constraint' }) {
artifactsInfo << [type: 'constraint', path: 'constraints', suffix: 'Constraint'] } }


This event listener will tell Griffon to look for anything under griffon-app/constraints folder with suffix 'Constraint' and register them as constraint artifacts.

Step 4 - Measure your artifacts

Now we pretty much have the basic bolts and nuts in place, its time to make our newly found artifact type to be more integrated with Griffon as any other first class artifact types do. One of the nice feature of Griffon is the stats command, it gives you an instant overview of how big your app is in terms of how many files and Line of Code per type including artifacts. Won't it be nice to have it also display the metrics about our own custom artifacts? fortunately its actually pretty easy to achieve in Griffon, similar to the previous step we will add another listener to the _Events script.


eventStatsStart = { pathToInfo ->
if(!pathToInfo.find{ it.path == 'constraints'} ) {
pathToInfo << [name: 'Constraints', path: 'constraints', filetype: ['.groovy','.java']] } }


Griffon event model is a very powerful concept, usually when I am not sure how to do something funky in Griffon this is the first place I look.

Step 5 - Automate your artifacts

One of big selling point of the next generation Rails-like RIA framework is the ability to create any artifact simply by using one of the built-in command, for example grails create-controller or griffon create-mvc. To make our new artifact type a true first-class citizen of Griffon, of course we need all the bells and whistles. To add a new command to Griffon, you need to create a new Groovy script under scripts folder:

CreateConstraint.groovy


includeTargets << griffonScript("_GriffonInit")
includeTargets << griffonScript("_GriffonCreateArtifacts")

target('default': "Creates a new constraint") {
depends(checkVersion, parseArguments)

promptForName(type: "Constraint")

def name = argsMap["params"][0]
createArtifact(name: name, suffix: "Constraint", type: "Constraint", path: "griffon-app/constraints")
createUnitTest(name: name, suffix: "Constraint")
}


Like other convention-over-configuration framework, Griffon relies heavily on simple naming conventions, so in the script make sure you naming everything consistent to avoid unnecessary complexity. This script will create artifact for the type of Constriant and related unit test case, as you can see it will be a simple matter to create integration test case if need be.

Now with the command in place, you can finally provide the template for the new artifact being created. Again naming convention is being used to determine where to find template file, for our example the template file should be placed under src/templates/artifacts and named Constraint.groovy:


@artifact.package@class @artifact.name@ {

def validate(propertyValue, bean, parameter) {
// insert your custom constraint logic
}

}


Phew, now finally we are done, hope I did not miss anything :) This is a long post and as you can see a lot of plumbing; this is exactly why currently there are some discussion going on within Griffon dev mail list to provide declaration based custom artifact support either using Grails style or Groovy AST transformation, so stay tuned for future updates on this topic.

Monday, February 21, 2011

Griffon Validation Plugin 0.6 Released

Griffon Validation Plugin v0.6 was released today. In this release the validation plugin's artifact handling has been rewritten to be compatible withe the up-coming Griffon 0.9.2 release. Also in this release validation plugin now simulates inheritance effect for the constraints you define. Since the constraints are defined using static fields following Grails convention, no real inheritance can be implemented however now with 0.6 release you class will basically copy the parent class' constraints, and additionally you can also override the parent constraint. See the following example:


@Validatable
class ServerParameter {
@Bindable String serverName
@Bindable int port
@Bindable String displayName

def beforeValidation = {
setDisplayName "${serverName}:${port}"
}

static constraints = {
serverName(nullable: false, blank: false)
port(range: 0..65535)
displayName(nullable: false, blank: false)
}
}

@Validatable
class ProtocolSpecificServerParameter extends ServerParameter{
@Bindable String protocol

def beforeValidation = {
setDisplayName "${protocol}://${serverName}:${port}"
}

static constraints = {
protocol(blank: false, nullable: false)
}
}


In the above example, the ProtocolSpecificServerParameter will not only inherent ServerParameter's serverName and port fields but also their associated constraints. The only resitration you need to be aware of is if the parent constraint generates error for a certain condition then the overriding child constraint has to generate error as well. In other words, validation plugin does not allow error hiding by using constraint override in the child class, similar to the method exception handling during inheritance within Java.

Sunday, February 20, 2011

JNDI Warrior v0.2 Released

JNDI Warrior v0.2 was released today. In this version you can now save your JNDI connection settings including it's classpath for future reuse. As well a simple sorting capability was added to the JNDI tree browser to help navigating through big JNDI tree commonly see in large enterprise. You can download the latest binary from here.

You are always welcome to use our Trac to submit any bug report or feature request.

Enhanced connection dialog: