Tuesday, May 27, 2008

Proof-of-Concept Backport Invokedynamic in Groovy

It is my first day of Google Summer of Code, and here is what I've done today ;-)

I have been reading hard and trying to understand Invokedynamic EDR since last week. It is quite difficult for me, who has little background of this kind of things. However, I have got some good sources for studying how invokedynamic work from John Rose's blog, Alex Tkachman's Method Handle code, Jeroen Frijters's blog, and Rémi Forax's blog.

Although, there is no something complex like Jeroen's sample, here is a working version of Hello World that uses my backport of invokedynamic APIs.


package groovy.dyn.subject.vm5;

import groovy.dyn.CallSite;
import groovy.dyn.Linkage;
import groovy.dyn.MethodHandle;
import groovy.dyn.MethodHandles;
import groovy.dyn.MethodType;
import groovy.dyn.StaticContext;
import groovy.dyn.internal.CallSiteImpl;
import groovy.dyn.internal.StaticContextImpl;

import java.io.PrintStream;
import java.lang.reflect.Method;

public class InvokeDynamicSubject {

private static CallSite[] callsites;
private static final int NUM_OF_CALLSITES = 1;

static {
initCallSites();
try {
Method method = InvokeDynamicSubject.class
.getDeclaredMethod("bootstrapInvokeDynamic",
CallSite.class, Object.class, Object[].class);
MethodHandle mh = MethodHandles.unreflect(method);
Linkage.registerBootstrapMethod(InvokeDynamicSubject.class, mh);
} catch (Throwable e) {
// do nothing now
}
}

private static void initCallSites() {
callsites = new CallSite[NUM_OF_CALLSITES];
callsites[0] = new CallSiteImpl(0,
new StaticContextImpl(
PrintStream.class,
"println",
MethodType.make(void.class, String.class)
)
);
}

private static Object bootstrapInvokeDynamic(CallSite cs,
Object receiver, Object[] args) throws Throwable {
Method method=null;
CallSiteImpl csi = (CallSiteImpl)cs; // down cast: language specific
switch(csi.index()) {
case 0: {
StaticContext sc = cs.getStaticContext();
Class<?> c = sc.getCallingClass();
method = c.getDeclaredMethod(
sc.getName(),
sc.getType().parameterType(1));
MethodHandle mh = MethodHandles.unreflect(method);
cs.setTarget(mh);
return mh.invoke(receiver, args);
}
}
return null;
}

public void test() throws Throwable {
// System.out.println("Hello World");
MethodHandle mh = callsites[0].getTarget();
if(mh == null) {
bootstrapInvokeDynamic(callsites[0], System.out, new Object[]{"Hello World"});
} else {
mh.invoke(System.out, "Hello World");
}
}
}

Sunday, May 18, 2008

Groovy, Bazaar and bzr-svn

In the past, I was wondering how could I manage my Groovy fork (SVN is not good to do that because it's centralized nature). I was thinking to use SVK, but failed to set it up.

Today, Russel posted to groovy-dev that he set up a branch of Groovy on Launchpad. I have heard about Launchpad, but never tried to be its user. More that 2 hours that I have tried to set everything up.

I registered to be a Launchpad user, then forked my branch from Russel's one. OK, then what's next?
I'm still on Windows XP, so I need a set of putty programs for SSH. Later, I use PUTTYGEN to generate my public key and import it into Launchpad.

The next step is to download Python 2.5, Bazaar for Windows. Then, I installed bazaar and was able to branch Russel's trunk locally.

bzr branch http://bazaar.launchpad.net/~russel/groovy/trunk



After that I pushed it back to my branch.

bzr push bzr+ssh://chanwit@bazaar.launchpad.net/~chanwit/groovy/ck1



Then, something went wrong. I did not setup SSH properly. bazaar blamed me that I forgot to set BZR_SSH.

set BZR_SSH=plink



OK, here we go. Now everything went smoothly. I can push my local codes back to Launchpad. So I did clean and re-build Groovy to see if everything was fine. But hey !, the branch of Russel is out-of-date. I need the latest patch Alex just committed. So what to do?

I checked some docs on bazaar wiki and they said it is able to merge code from SVN using bzr-svn plugin. That sounds great to me. So I downloaded bzr-svn, but thing did not go smooth again. I need Python-Subversion binding with the special patches. Come on, where on the earth it is? Alright ! It seems I got everything to run bzr-svn.

I ran this command:

bzr merge http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/



but it was not successful. The document said it should be svn+http. How silly I am. I tried merging again:

bzr merge svn+http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/



And it worked ! After that I did commit.

bzr commit -m "message goes here"



and push all committed code back to Launchpad again:

bzr push



Wow, it was fun and cool!. Now I can have my experiment branch without missing any new patch from the main trunk! That's great, isn't it?

Wednesday, March 26, 2008

My SPLAT 2008 Homework

Using Groovy AOP to express business logics in Grails framework

This document describes a number of problems found in the scenario of applying Groovy AOP to Grails framework that reflects Comprehensibility.

From http://grails.org,

"Grails aims to bring the "coding by convention" paradigm to Groovy. It's an open-source web application framework that leverages the Groovy language and complements Java Web development. You can use Grails as a standalone development environment that hides all configuration details or integrate your Java business logic. Grails aims to make development as simple as possible and hence should appeal to a wide range of developers not just those from the Java community."

There are high-level artefacts e.g., Controllers, or Domain classes in Grails framework. An AOP language needs a good design to cover high-level pointcuts for expressing such software artefacts in Grails. Moreover, Grails is extensible via its plug-in system. A developer can define their own artefacts in a newly developed plug-in. For Grails, the AOP language needs to be extensible to capture join points introduced by these artefacts.

For example, Groovy AOP comes with a set of primitive pointcuts like:
- pcall(pattern)
- etc.

But these do not reflect the semantics of Grails artefacts. Their users need some PCDs like:
- controller(pattern)
- action(pattern)
- etc.

Here is an example of a web application written using Grails.

class BookController {
def list = {
def books = Book.list()
render(view:'book', model:[books: books])
}

def show = {
...
}

def update = {
...
}
}



An aspect written in Groovy AOP:

aspect SecurityAspect {
def pc = pcall('BookController.*')
around(pc) {
// check access
}
}



An aspect written in Groovy AOP, with a higher level of PCDs:

aspect SecurityAspect {
def pc = controller('book') & action('*')
around(pc) {
// check access
}
}



An aspect written using a variant syntax Groovy AOP, with a higher level of PCD (shortcut form):
This syntax is probably preferred by the developers, according to idioms they are familiar with.

aspect SecurityAspect {
around(controller:'book', action:'*') {
// check access
}
}



Groovy AOP also needs to support a good way to be able to extend and cover a new software artefact added through Grails plugin system as well.

Conclusion

As a general purpose AOP system for Groovy, Groovy AOP can be used with Grails framework. It may help expressing business logics as application-level aspects. However, the implementation of Grails framework is dynamic because a developer can add a new software artefact by creating a plug-in. To make an AOP system meets this requirement, it should support the ability to define new PCDs through the same plug-in architecture.

Ability to understand by developers ?
- A system should provide idioms that developer are familiar with in the AOP language
Ability to evolve & customize ?
- A system should provide the way to define new PCDs
How well does AOSD support/go along with Comprehensibility?
- Depends on its implementation, and its base language (e.g. Java / AspectJ, Groovy / Groovy AOP). Currently, Groovy AOP has not support this approach well yet.

Sunday, March 23, 2008

Composite Id and FK as PK in GORM

I've been discussing with my colleage about how to map 2 classes in GORM. It's a bit complicated, where we need to use the foreign key from one, Process, class to be the primary key for another class, State.

Code listing as follows:

class Process implements Serializable {

String name
static hasMany = [states: State]

}



class State {

String stateId
Process process

static mapping = {
version false
id composite:['stateId','process'], generator:'assigned'
}

}



Don't forget to use obj.save(insert:true), when you have a GORM mapping with version=false and generation='assigned'.

You see, life is much more easier with GORM.