Alfresco and Groovy, Baby!

cancel
Showing results for 
Search instead for 
Did you mean: 

Alfresco and Groovy, Baby!

pmonks2
Member II
0 33 25K
For quite a few years now I've been a fan of scripted languages that run on the JVM, initially experimenting with the venerable BeanShell, then tinkering with Javascript (via Rhino), JRuby and finally discovering Groovy in late 2007.  A significant advantage that Groovy has over most of those other languages (with the possible exception of BeanShell), is that it is basically a superset of Java, so most valid Java code is also valid Groovy code and can therefore be executed by the Groovy 'interpreter'1 without requiring compilation, packaging or deployment - three things that significantly drag down one's productivity with 'real' Java.



To that end I decided to see if there was a way to implement Alfresco Web Scripts using Groovy, ideally in the hope of gaining access to the powerful Alfresco Java APIs with all of the productivity benefits of working in a scripting-like interpreted environment.



It turns out that the Spring Framework (a central part of Alfresco) moved in this direction some time ago, with support for what they refer to as dynamic-language-backed beans.  Given that a Java backed Web Script is little more than a Spring bean plus a descriptor and some view templates, initially it seemed like Groovy backed Web Scripts might be possible in Alfresco already, merely by adding the Groovy runtime JAR to the Alfresco classpath and then configuring a Java-backed Web Script with a dynamic-language-backed Spring bean.

Oh behave!



Unfortunately this approach ran into one small snag: Alfresco requires that Java Web Script beans have a 'parent' of 'webscript', as follows:



<bean id='webscript.my.web.script.get'

class='com.acme.MyWebScript'

parent='webscript'>

<constructor-arg index='0' ref='ServiceRegistry' />

</bean>



but Spring doesn't allow dynamic-language-backed beans to have a 'parent' clause.

It's freedom baby, yeah!



There are several ways to work around this issue, but the simplest was to implement a 'proxy' Web Script bean in Java that simply delegates to another Spring bean, which itself could be a dynamic-language-backed Spring bean implemented in any of the dynamic languages Spring supports.



This class ends up looking something like (imports and comments removed in the interest of brevity):



public class DelegatingWebScript

extends DeclarativeWebScript

{

private final DynamicDeclarativeWebScript dynamicWebScript;








public DelegatingWebScript(final DynamicDeclarativeWebScript dynamicWebScript)

{

this.dynamicWebScript = dynamicWebScript;

}








@Override

protected Map executeImpl(WebScriptRequest request, Status status, Cache cache)

{

return(dynamicWebScript.execute(request, status, cache));

}

}



While DynamicDeclarativeWebScript looks something like:



public interface DynamicDeclarativeWebScript

{

Map execute(WebScriptRequest request, Status status, Cache cache);

}



This Java interface defines the API the Groovy code needs to implement in order for the DelegatingWebScript to be able to delegate to it correctly when the Web Script is invoked.



The net effect of all this is that a Web Script can now be implemented in Groovy (or any of the dynamic languages Spring supports for beans), by implementing the DynamicDeclarativeWebScript interface in a Groovy class, declaring a Spring bean with the script file containing that Groovy class and then configuring a new DelegatingWebScript instance with that dynamic bean.  This may sound complicated, but as you can see in this example, is pretty straightforward:



<lang:groovy id='groovy.myWebScript'

refresh-check-delay='5000'

script-source='classpath:alfresco/extension/groovy/MyWebScript.groovy'>

<langSmiley Tongueroperty name='serviceRegistry' ref='ServiceRegistry' />

</lang:groovy>








<bean id='webscript.groovy.myWebScript'

class='org.alfresco.extension.webscripts.groovy.DynamicDelegatingWebScript'

parent='webscript'>

<constructor-arg index='0' ref='groovy.myWebScript' />

</bean>





While a little more work than I'd expected, this approach meets all of my goals of being able to write Groovy backed Web Scripts, and in the interests of sharing I've put the code up on the Alfresco forge Google Code.

I demand the sum... ...OF 1 MILLION DOLLARS!



But wait - there's more! Not content with simply providing a framework for developing custom Web Scripts in Groovy, I decided to test out this framework by implementing a 'Groovy Shell' Web Script.  The idea here is that rather than having to develop and register a new Groovy Web Script each and every time I want to tinker with some Groovy code, instead the Web Script would receive the Groovy code as a parameter and execute whatever is passed to it.



Before we go any further, I should mention one very important thing: this opens up a massive script-injection-attack hole in Alfresco, and as a result this Web Script should NOT be used in any environment where data loss (or worse!) is unacceptable!! It is trivial to upload a script that does extremely nasty things to the machine hosting Alfresco (including, but by no means limited to, formatting all drives attached to the system) so please be extremely cautious about where this Web Script gets deployed!



Getting back on track, I accomplished this using Groovy's GroovyShell class to evaluate a form POSTed parameter to the Web Script as Groovy code (this is conceptually identical to Javascript's 'eval' function, hence the warning about injection attacks).  Effectively we have a Groovy-backed Web Script that interprets an input parameter as Groovy code, and then goes ahead and dynamically executes it!  It's turtles all the way down!



The code also transforms the output of the script into JSON format, since there are existing Java libraries for transforming arbitrary object graphs (as would be returned by an arbitrary Groovy script) into JSON format.



Here's a screenshot showing the end result:



[caption id='attachment_274' align='aligncenter' width='500' caption='Alfresco Groovy Shell - Vanilla Groovy Script']Alfresco Groovy Shell[/caption]



The more observant reader will have noticed the notes in the top right corner, particularly the note referring to a 'serviceRegistry' object.  Before evaluating the script, the Web Script injects the all important Alfresco ServiceRegistry object into the execution context of the script, in a Groovy variable called 'serviceRegistry'.  The reason for doing so is obvious - this allows the script to interrogate and manipulate the Alfresco repository:



[caption id='attachment_276' align='aligncenter' width='500' caption='Alfresco Groovy Shell - Groovy Script that Interrogates the Alfresco Repository']Alfresco Groovy Shell[/caption]

Sharks with lasers strapped to their heads!



Now if you look carefully at this script, you'll notice that it (mostly) looks like Java, and this is where the value of this Groovy Shell Web Script starts to become apparent: because most valid Java code is also valid Groovy code, you can use this Web Script to prototype Java code that interacts with the Alfresco repository, without going through the usual Java rigmarole of compiling, packaging, deploying and restarting!



I recently conducted an in-depth custom code review for an Alfresco customer who had used Java extensively, and this Web Script was a godsend - not only did I eliminate the drudgery of compiling, packaging and deploying the customer's custom code (not to mention restarting Alfresco each time), I also completely avoided the time consuming (and, let's be honest, painful) task of trying to reverse engineer their build toolchain so that I could build the code in my environment.  This alone was worth the price of admission, but coupled with the rapid turnaround on changes (the mythical 'edit / test / edit / test' cycle), I was able to diagnose their issues in a much shorter time than would otherwise have been possible.

Conclusion



As always I'm keen to hear of your experiences with this project should you choose to use it, and am keen to have others join me in maintaining and enhancing the code (which is surprisingly little, once all's said and done).




Technically Groovy does not have an interpreter; rather it compiles source scripts into JVM bytecode on demand.  The net effect for the developer however is the same - the developer doesn't have to build, package or deploy their code prior to execution - a serious productivity boost.
33 Comments
blog_commenter
Active Member
Extremely useful article, thank you Peter! Opens up many possibilities for much faster development / test / deployment process.
pmonks2
Member II
Thanks Maurizio!  Yeah I'm also keen to try this out with some of the other dynamic languages that Spring supports (e.g. JRuby).  I can't see any reasons why it wouldn't work with the current code, although of course you'd lose the (valuable!) ability to simply paste in Java code and have it interpreted as Groovy.
blog_commenter
Active Member
This is so nice and I have been waiting for it a long time.  If I use Groovy to develop webscripts in Alfresco or Surf, does it have access to the root-scoped objects that make up the Surf and Repository JavaScript APIs?  Objects like 'companyhome', 'person', 'space', etc?
blog_commenter
Active Member
Let me reformulate my question a bit.  I know I can query this information using the Alfresco public services, but are these objects exposed as root objects in the Groovy scripts I write?
pmonks2
Member II
Darryl, developing a Groovy-backed Web Script is identical to developing a Java-backed Web Script, except that Groovy is the implementation language instead of Java.  What this means is that the Groovy class (via the proxy Java class described above) ends up being a DeclarativeWebScript, with exactly the same interface and state available to it as would be available to a native Java implementation.



It does not attempt to reproduce the higher level Javascript runtime context - the 'companyhome', 'person', 'space' etc. root scope objects.  Instead it's intended to provide the same power and functionality as native Java Web Scripts, but with a more productive scripting-like development cycle.  For this reason the code deliberately doesn't embellish the native repository APIs with higher level abstractions (as is done in the Javascript and Freemarker contexts), since that kind of development experience is already available via Javascript-backed Web Scripts.
blog_commenter
Active Member
Darryl,



As Peter suggested, these bindings are available in other contexts. 



However, it would be pretty easy to add additional variables into the context of your shell.   When you create the GroovyShell, you just need to pass in a Binding parameter.  I don't remember off the top of my head how to get access to company home, but assuming you have a method getCompanyHome(), this would add it to the groovy shell context: 



   shell = new GroovyShell(grailsApplication.classLoader, new Binding(

            'companyhome': getCompanyHome() )
blog_commenter
Active Member
In general, if you are interested in using Alfresco with Groovy / Grails, there is an Alfresco plugin for Grails - it allows you to connect to the Alfresco server, and has some taglibs for displaying stored content.  



http://grails.org/plugin/alfresco
pmonks2
Member II
As Jean points out, it is easy to add additional bindings (on top of the 'serviceRegistry' binding that's already provided), however my intent is to mirror as closely as possible the Java execution context, rather than the higher level Javascript execution context.  This makes it easier to use the Groovy Shell Web Script as a rapid prototyping tool for Java code, in part because it prevents that Java code from becoming dependent on state that isn't normally available to Java code (i.e. custom root scope objects).



Plus it helps to reinforce the official approach to high level Web Script development in Alfresco i.e. using Javascript + Freemarker.
blog_commenter
Active Member
I knew I had read something similar somewhere quite a long time ago:

It's not the same thing but it's related:

http://thinkalfresco.blogspot.com/2009/01/groovy-scripting-for-alfresco-alf-hack.html



What other languages does spring 'support' ? What about PHP ?

AFAIK PHP can be used as a template engine (using Quercus I guess) instead of freemarker and scripting framework is supposed to be extensible. Would the Java foundation API be accesible through Quercus ? Does anyone have any experience or reference about it.



Nice article, thanks.
pmonks2
Member II
Igor, as of Spring 2.0 (which is the version Alfresco currently embeds), the following languages can be used for dynamic-language-backed beans: JRuby, Groovy and BeanShell.





blog_commenter
Active Member
Hallo everybody,



guess the route to follow pretty much depends on what your trying to accomplish.



I've been using the approach mentioned by Igor to apply what the Canoo people call 'endoscopic operation'. In general that is not related to alfresco at all : http://docs.codehaus.org/display/GROOVY/Embedding+a+Groovy+Console+in+a+Java+Server+Application - it basically allows you to 'mess with the application' at runtime.



If what you want is implementing webscripts in groovy, guess Peters DynamicDelegatingWebScript is the way to go.



cheers

Andreas
blog_commenter
Active Member
blog_commenter
Active Member
Looks interesting.  Could you give more details about the Groovy shell?

It doesn't look like there are any files posted on Alfresco Forge project.

Thanks!
pmonks2
Member II
George, the GroovyShell class used by the example Web Script is described here.  Basically it's a programmatically accessible version of the Groovy 'interpreter'.



Currently there aren't any binaries available for download.  The source code, however, is available via the Alfresco forge project.
blog_commenter
Active Member
Thanks for the quick response.  That Source Forge project doesn't have any files uploaded for it.

If you get a chance, could you please upload the source?

Thanks!
pmonks2
Member II
George, the source code is already available on the Alfresco forge.  To check it out, create a new empty directory, then run the following command from a shell window / command prompt from within that directory:



svn co https://forge.alfresco.com/svn/groovy/branches/3.1

blog_commenter
Active Member
Hi Peter,

Thanks for your help.  I wasn't logged in when I looked at the forge site, so no files were available.  I registered and was then able to see the files.  I do have the files now, but when I try to do a maven build it complained about needing authentication to access. Do I need to somehow register for that repository too?  The alfresco-employee-repository is marked as 'Private'.   Thanks



[WARNING] Unable to get resource 'org.alfresco.maven.plugin:maven-amp-pluginSmiley Tongueom

:3.0.2' from repository alfresco-employee-repository (https://svn.alfresco.com/r

epos/field/maven): Authorization failed: Access denied to: https://svn.alfresco.

com/repos/field/maven/org/alfresco/maven/plugin/maven-amp-plugin/3.0.2/maven-amp

-plugin-3.0.2.pom
pmonks2
Member II
George, the Alfresco Enterprise artifacts are currently only available on an employee-only Maven repository.  To compile the package yourself, you'll either need to create a local repository containing the Alfresco Enterprise artifacts, or switch the repositories & dependencies in the pom.xml to point to the Alfresco Community artifacts (which are available on maven.alfresco.com).
blog_commenter
Active Member
Hi Peter, thanks for providing this cool groovy console. Ca you provide an amp-file for alfresco 3.4a Community?
pmonks2
Member II
Jens, currently the code won't compile or run against 3.3+, due to the non-backwards compatible changes in the Web Script framework introduced as part of the donation of Surf to Spring.  If you're interested in upgrading the code, let me know!
blog_commenter
Active Member
Hello Peter,

yes, I am interested into upgrading it. But I can only do it in my freetime, so maybe it will take some days.
pmonks2
Member II
Jens, that'd be great!  This is a hobby project for me too, so I'm unlikely to get to it any faster than you would.



When / if you finish the work, might I suggest you email me a patch / diff file so that I can merge it into the source tree?  My email address (reversed) is moc DOT ocserfla AT sknomp
blog_commenter
Active Member
Hi Peter,

very interesting indeed but we definitely need to make it run under 3.4b.

If you need some help with that, feel free to drop me a line ;-)



My reversed email is com DOT gmail AT cmorgia.



Cheers,

Claudio
pmonks2
Member II
Claudio, once 3.4 Enterprise is released I'll be able to start working on a port for it.  Of course don't let that stop you working on a port for 3.4 Community in the meantime!
pmonks2
Member II
Just a head's up that I've checked in a 3.3 version of the tool.  This should also work on 3.4, but hasn't been tested on that version yet.
blog_commenter
Active Member
Hi Peter,



   I went to the forge page of your project and didn't find any file to download.  I'm really looking forward to use this tool.  We are using Alfresco 3.4.  How the development/testing going on that version?
pmonks2
Member II
CharlesV, currently the tool is provided in source form only - I haven't had time to prepare and distribute a binary for it.



The tool works unchanged on both Alfresco v3.3 and v3.4 - in fact I've been using it on Alfresco Enterprise 3.4SP1 in just the last couple of weeks.
blog_commenter
Active Member
[...] Alfresco and Groovy, Baby! [...]
pmonks2
Member II
Please note that the code for this project has moved to Google Code.
blog_commenter
Active Member
Practice safe Groovy... use Groovy++ :-)  (http://code.google.com/p/groovypptest/)

BTW... this is great. I was wondering why it has not done before. I would not use Groovy for a large project, but there are just so many things to like about it!
pmonks2
Member II
Luis, the package should support any language that:



1. can be registered via Spring's 'dynamic language bean' mechanism

2. can implement a Java interface



Spring only includes support for JRuby, Groovy and BeanShell, but as mentioned on their page:



There is nothing stopping the inclusion of further languages though. If you want to see support for <insert your favourite dynamic language here>, you can always raise an issue on Spring's JIRA page (or implement such support yourself).

pmonks2
Member II
Oh and it's also worth mentioning that Alfresco Community member Carlo Sciolla has implemented support for Clojure-backed Web Scripts, as well as providing an idiomatic Clojure facade for the Alfresco Java APIs.  Definitely worth checking out!
blog_commenter
Active Member
[...] Alfresco and Groovy, Baby! [...]