0

This follows from my unsuccessful attempt to find an answer to this question from 2014.

It's not clear to me whether there might in fact be some techniques in Groovy to use closures, specifically, to hide information. All I can say is that if information on such techniques is out there it is a perfect illustration, precisely, of "information-hiding": I cannot find it!

But failing that I think my understanding now is that absolutely zero attempt to hide information (or pretend to, as in Java - bearing in mind reflection techniques) is ever made. This appears to be by design, but also due to the requirements of Groovy's dynamic nature. It seems, for example, that @CompileStatic, mentioned in the referenced question, is more about type-checking than anything else.

But in Python, for example, there is a convention (I assume still used) to make "fields which are meant to be considered private" begin with a double underscore. I've never heard anyone talking about this in connection with Groovy.

Aren't information-hiding and encapsulation, or at least conventions to encourage disciplined use of the "intimate state" of objects, good things? Any Groovy experts care to comment?

later

daggett has given an answer which is interesting in some ways, but not really what I had in mind. Consider this:

class Main {
    static main( args ) {
        def sm = new SecurityManager()
        System.setSecurityManager( sm )
        println new Bob().doSomethingProtected()
    }
}


class Bob {
    public doSomethingPublic() {
        "public"
    }
    private doSomethingPrivate() {
        "private"
    }
    protected doSomethingProtected() {
        "protected"
    }
}

... whichever one of these Bob methods is called it will pass with the SecurityManager not set, but fail with it set. It also doesn't matter which package it's in. Nor does it matter whether Bob is in a subpackage (for example), with @PackageScope: it is only if Main.main is given @CompileStatic that this will help (see referenced question). I'm also not clear about precisely what you can do with a SecurityManager set in this way: is it possible to enforce private or protected (or package-private) in some way? At the moment I just don't know and will have to investigate.

As for the other suggestion, it's intriguing, but doesn't in fact deny "visibility" as suggested. You'd also need to include the following method in class A:

def getI() {
    throw new Exception()
}

After that, yes, visibility is denied to every other class, whether in the same package or not, and also these "private" elements are not even visible to other objects of the same class (! - unlike Java). In that sense it does indeed deliver a very Draconian privacy. But to me it's also a bit of a hack. I'm not quite clear about this GroovyObjectSupport class or what it does, and will have to investigate it. Finally, there is little point in actually giving these fields the private modifier. As I said, the ONLY function of private in Groovy is to deny visibility of these fields to subclasses of daggett's class A here.

Having only a stark choice between a super-Draconian and hackish "private", or "unrestrictedly public", clearly represents a considerable "impoverishment" of choice of visibility compared to Java, where you have not only protected but also package-private (subject, yes yes yes, of course, to use of reflection...), and where private fields are visible to other objects of the same class.

mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • at runtime groovy uses reflection to call methods. in java you can call protected and private methods using reflection. if you want to disable it you have to use [Security Features in Java](https://docs.oracle.com/javase/tutorial/security/index.html). check the `suppressAccessChecks` permission target in [ReflectPermission](https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/ReflectPermission.html) – daggett Jun 12 '18 at 21:19
  • Thanks... I was aware of those features for Java although I've never used them. Wouldn't using that technique interfere with other features of Groovy, relating to the MOP, etc.? Going to investigate though... – mike rodent Jun 13 '18 at 07:00
  • I've had a first look at Java security features. I'm not sure how you get Groovy to use them (e.g. `-Djava.security.manager` is not a valid switch for the `groovy` command) ... can I just ask whether you have actually used these features for this purpose (information-hiding), or is it just a sort of idea about how it might be done? – mike rodent Jun 14 '18 at 08:10

2 Answers2

1

SecurityManager

don't run following code from GroovyConsole. only from groovy command line.

def sm = new SecurityManager()
System.setSecurityManager(sm)
//without previous lines the following code will run successfully
println new ByteArrayOutputStream().buf

this will throw the following exception

Caught: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:109)
        at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:131)
Caused by: java.lang.ExceptionInInitializerError
        at groovy.ui.GroovyMain.run(GroovyMain.java:397)
        at groovy.ui.GroovyMain.process(GroovyMain.java:370)
        at groovy.ui.GroovyMain.processArgs(GroovyMain.java:129)
        at groovy.ui.GroovyMain.main(GroovyMain.java:109)
        ... 6 more
Caused by: java.security.AccessControlException: access denied ("java.util.logging.LoggingPermission" "control")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.util.logging.LogManager.checkPermission(LogManager.java:1586)
        at java.util.logging.Logger.checkPermission(Logger.java:422)
        at java.util.logging.Logger.setUseParentHandlers(Logger.java:1799)
        at org.codehaus.groovy.runtime.StackTraceUtils.<clinit>(StackTraceUtils.java:57)
        ... 10 more

control access with getProperty & setProperty

class A extends GroovyObjectSupport{
    private int i=555
    private int j=666
    def f(){
        println "i=$i j=$j"
    }
    Object getProperty(String name){
        if(name in ['i'])throw new Exception("Access to property `$name` is denied")
        return super.getProperty(name)
    }
}
def a=new A()
a.f()
println "a.j = ${a.j}"
println "a.i = ${a.i}"

this will allow access to member j but not to the member i outside of class.

output:

i=555 j=666
a.j = 666
Exception thrown

java.lang.Exception: Access to property `i` is denied
  ...
daggett
  • 26,404
  • 3
  • 40
  • 56
  • Thanks... this is an interesting suggestion in some ways, but it is not what I had in mind. See my comments in an update to the question. Thanks for showing how to use SecurityManager in a groovy program though. – mike rodent Jun 14 '18 at 18:10
0

I've created a compiler extension that allows you to either selectively or globally apply compile-time checks for violation of encapsulation.  Additionally, you can enforce strong type checking and immutability checks at compile time. https://github.com/stansonhealth/ast-framework

An example:

            package com.stansonhealth.ast.encapsulate
            import com.stansonhealth.ast.encapsulate.Encapsulate

            @Encapsulate
            class TestEncapsulationFixture {
                private int i
            }
            
            class TestEncapsulation {
                void foo(TestEncapsulationFixture fixture) {
                    fixture.i++
                    fixture[i]++
                    fixture.i = 0
                    def x = fixture.i
                }
            }

Compiler output:

startup failed:
TestEncapsulation: 14: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
 @ line 14, column 21.
                       fixture.i++
                       ^

TestEncapsulation: 15: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
 @ line 15, column 21.
                       fixture[i]++
                       ^

TestEncapsulation: 16: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
 @ line 16, column 21.
                       fixture.i = 0
                       ^

TestEncapsulation: 17: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
 @ line 17, column 29.
                       def x = fixture.i
                               ^

4 errors