About Me

My photo
Author of Groovy modules: GBench, GProf, Co-author of a Java book パーフェクトJava, Game Developer at GREE

Tuesday, January 11, 2011

Groovy doesn't have access control, so what do we do?

In Groovy, for now version 1.7, access modifiers don't work. They only play a part in calling our attention, same as comments. See the following example. If access modifiers don't give a false name, accessing to the class members should be restricted.
----
package foo


class Foo {
    protected protectedField
    @PackageScope packagePrivateField
    private privateField


    protected protectedMethod() { }
    private privateMethod() { }
}
----

To ensure that Groovy compiles the Groovy code to Java bytecode as expected, let's disassemble the class file.
----
javap -private Foo
----

The result will be this. No problems. Perfect. It looks as expected.
----
public class Foo extends java.lang.Object implements groovy.lang.GroovyObject{
    protected java.lang.Object protectedField;
    java.lang.Object packageScopeField;
    private java.lang.Object privateField;
    :
    protected java.lang.Object protectedMethod();
    private java.lang.Object privateMethod();
}
----

But accessing them is not restricted. You can access them from anywhere like the following example.
----
package bar


def foo = new Foo()
foo.protectedField
foo.packagePrivateField
foo.privateField
foo.protectedMethod()
foo.privateMethod()
----

According to http://jira.codehaus.org/browse/GROOVY-1875, This bug will be fixed in version 2.0. So what can we do about it now?

What about naming rule? It's probably almost enough to protect from accidents. For example, use underscore prefix like Python or Perl.
----
protected _protectedField
@PackageScope __packageScopeField
private ___privateField


protected _protectedMethod()
private ___privateMethod()
----

It's dirty, complicated. I don't want to use it.

What about closure? If you only need private scope, you can realize it by using closure.
----
package foo


class SecretiveFoo {
  
    SecretiveFoo() {
        def privateField;
        getPrivateField = {
            privateField  
        }
        setPrivateField = { newValue ->
            privateField = newValue
        }
      
        def privateMethod = {
            println 'private method called'
        }
        publicMethod = {
            privateMethod.call()
            // do others
        }
    }
  
    public getPrivateField
    public setPrivateField
    public publicMethod
}
----

----
package bar


def secretiveFoo = new SecretiveFoo()
secretiveFoo.setPrivateField(1)
println secretiveFoo.getPrivateField()
secretiveFoo.publicMethod()
----

The result will be this.
----
1
private method called
----

But this approach costs too much. You can see it by running the following program. Transparent is a normal Groovy class. Secretive is a class that simulates using the approach and generates specified number of closures that accesses a private field. It measures the cost to instantiate them 1000 times.
----
class Secretive {
  
    Secretive(n) {
        def privateField = 0
        actions = []
        (0..n).each {
            actions << { privateField }  
        }
    }
  
    def actions
}


class Transparent {
  
    private privateField = 0
  
}


def maxmem = Runtime.runtime.maxMemory()
def memBefore = maxmem - Runtime.runtime.freeMemory()
def timeBefore = System.nanoTime()
def list = []
(0..<1000).each {
    list << new Secretive(10)
    // list << new Transparent()
}
def timeAfter = System.nanoTime()
def memAfter = maxmem - Runtime.runtime.freeMemory()
def diff = memAfter - memBefore
println "time usage: ${ (timeAfter - timeBefore) * 0.000001 } ms"
println "memory usage: ${ (memAfter - memBefore) * 0.001 } kb"
----

I got the following results. Too heavy. I cannot use it.

TransparentSecretive(10)Serective(20)Serective(30)
Average Time Usage (ms)17.2662.8375.6687.15
Average Memory Usage (kb)1133.672706.193720.965073.95

All we can do is just sit and wait for 2.0 release?

Any idea?

   

2 comments:

  1. I might be wrong, but I always thought the standard line was that you turn on the Java Security Manager at runtime. I will have to look into this JIRA issue because this feature can be very useful at times, especially when writing tests against existing codebases.

    ReplyDelete
  2. That sounds good. I'll try it soon. Thank you.

    ReplyDelete