grails-core icon indicating copy to clipboard operation
grails-core copied to clipboard

Domain class variable not initialized if final when accessed from Java class

Open torbjorn-boost opened this issue 2 years ago • 17 comments

Expected Behavior

I've upgraded from Grails 4 to 5, and from Java 8 to 17, and this started happening:

Having this domain class:

package myapp

import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic
class Role {

    public final static long ADMIN = 1
    public final static long USER = 2

    ...
}

... and this Java service:

package myapp;

import myapp.Role;

public class RoleService {
    public void doWhatever() {
        System.out.println(Role.ADMIN);
        System.out.println(Role.USER);
    }
}

... I would expect the code to print 1 and 2.

Actual Behaviour

It prints 0 and 0, as though the variables have not been initialized.

If I print the variables from a static initializer in the Domain class, like this:

package myapp

import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic
class Role {

    public final static long ADMIN = 1
    public final static long USER = 2

    static {
        System.out.println(ADMIN);
        System.out.println(USER);
    }
    ...
}

... it prints 1 and 2, so it seems like something's preventing my Java service from "seeing" the variables.

Also note that this only happens as long as the class variables are final. If I make them non-final, things work as before.

Steps To Reproduce

No response

Environment Information

Kubuntu 18.04 JDK: corretto-17 Gradle wrapper version: 7.4.1

grailsVersion=5.1.3 gormVersion=7.2.0 groovyVersion=3.0.10

Example Application

No response

Version

5.1.3

torbjorn-boost avatar Mar 29 '22 11:03 torbjorn-boost

See the project at github.com/jeffbrown/torbjorn-boost-constants.

grails-app/domain/torbjorn/boost/constants/Role.groovy

package torbjorn.boost.constants

import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic
class Role {
    public final static long ADMIN = 1
    public final static long USER = 2
    static {
        System.out.println(ADMIN);
        System.out.println(USER);
    }
}

src/main/groovy/torbjorn/boost/constants/RoleService.java

package torbjorn.boost.constants;

public class RoleService {
    public void doWhatever() {
        System.out.println(Role.ADMIN);
        System.out.println(Role.USER);
    }
}

grails-app/controllers/torbjorn/boost/constants/DemoController.groovy

package torbjorn.boost.constants

class DemoController {
    RoleService roleService
    def index() {
        roleService.doWhatever()
        render 'Success'
    }
}

That appears to work as expected. Can you test that in your environment and report back to indicate whether that code misbehaves in your environment?

osscontributor avatar Mar 29 '22 15:03 osscontributor

it prints 1 and 2, so it seems like something's preventing my Java service from "seeing" the variables.

I think the fact that it is printing 1 and 2 is evidence that it is seeing the variables.

osscontributor avatar Mar 29 '22 16:03 osscontributor

A note that grails 5 is not compatible with jdk 17

fernando88to avatar Mar 29 '22 20:03 fernando88to

See the project at github.com/jeffbrown/torbjorn-boost-constants.

Thanks, I'll check it out!

it prints 1 and 2, so it seems like something's preventing my Java service from "seeing" the variables.

I think the fact that it is printing 1 and 2 is evidence that it is seeing the variables.

Yeah, the domain class is seeing the variables correctly, but the Java service is not. Even if the static initializer has just executed and printed 1 and 2, the Java service sees 0 and 0.

A note that grails 5 is not compatible with jdk 17

Oh, I wasn't aware! I didn't find anything in the documentation other than that I needed 8+. I figured since I could run Gradle 7.3 everything was good :)

torbjorn-boost avatar Mar 30 '22 06:03 torbjorn-boost

@jeffbrown I cloned your project, and was able to reproduce my problem, sort of ... I had to upgrade Groovy and Gradle to get it to run, because of my JDK 17 (for the record, 17.0.2 (Amazon.com Inc. 17.0.2+8-LTS)), and I clearly that's what's causing the problem. Making the following changes:

  • groovyVersion 3.0.7->3.0.10
  • distributionUrl https://services.gradle.org/distributions/gradle-7.2-bin.zip->https://services.gradle.org/distributions/gradle-7.4.1-bin.zip

... and the same thing happens:

image

If JDK 17 isn't supposed to be supported, I guess this isn't a bug, but maybe it's still interesting?

torbjorn-boost avatar Mar 30 '22 07:03 torbjorn-boost

If JDK 17 isn't supposed to be supported, I guess this isn't a bug

Agreed

but maybe it's still interesting?

It is. Thank you for the feedback.

osscontributor avatar Mar 31 '22 13:03 osscontributor

Even if the static initializer has just executed and printed 1 and 2, the Java service sees 0 and 0.

@torbjorn-boost I cannot reproduce that.

When I run the project at github.com/jeffbrown/torbjorn-boost-constants and send a request to /demo the output shows 1 and 2. A request to /demo will be handled by grails-app/controllers/torbjorn/boost/constants/DemoController.groovy. That code is delegating to the Java service at src/main/groovy/torbjorn/boost/constants/RoleService.java and does not appear to see 0 and 0.

osscontributor avatar Apr 07 '22 13:04 osscontributor

Can you clarify if you are seeing this problem with Java 8?

osscontributor avatar Apr 07 '22 13:04 osscontributor

@jeffbrown Breaking news!

Trying some different combinations of JDK, gradle and groovy versions, I found out that this error manifests (i.e. shows 1 2 on boot, but 0 0 when calling /demo) regardless of JDK version (8, 11 or 17) as long as I'm using Groovy 3.0.10. Changing back to 3.0.7 fixes it (i.e. shows 1 2 both on boot and when calling /demo), as does 3.0.9.

Does this make this a potential regression in Groovy 3.0.10?

torbjorn-boost avatar Apr 25 '22 08:04 torbjorn-boost

I've created the most minimal reproduction case I could here: https://github.com/torbjorn-boost/groovy-static-field, and I'll file a thing with Groovy.

torbjorn-boost avatar Apr 29 '22 11:04 torbjorn-boost

Does this make this a potential regression in Groovy 3.0.10?

Yes. I don't know that it is a regression in 3.0.10 but I think it is a potential regression in 3.0.10.

osscontributor avatar May 04 '22 14:05 osscontributor

I have created a project which eliminates domain classes and Grails as a factor. See github.com/jeffbrown/issue12451.

app/build.gradle#L10-L15

dependencies {
    // 3.0.9 appears to yield correct behavior
//    implementation 'org.codehaus.groovy:groovy-all:3.0.9'

    implementation 'org.codehaus.groovy:groovy-all:3.0.10'
}

app/src/main/groovy/issue12451/App.groovy

package issue12451

class App {

    static void main(String[] args) {
        new JavaHelper()
        new GroovyHelper()
    }
}

https://github.com/jeffbrown/issue12451/blob/b553d53d1224225556af6e4776eab4bea45407a8/app/src/main/groovy/issue12451/JavaHelper.java

package issue12451;

public class JavaHelper {
    public JavaHelper() {
        System.out.printf("JavaHelper() sees %d%n", GroovyHelper.STATIC_LONG);
    }
}

app/src/main/groovy/issue12451/GroovyHelper.groovy

package issue12451

class GroovyHelper {
    public final static long STATIC_LONG = 123

    GroovyHelper() {
        println "GroovyHelper() sees $STATIC_LONG"
    }
}

I believe that demonstrates the behavior in question:

~ $ git clone [email protected]:jeffbrown/issue12451.git
Cloning into 'issue12451'...
remote: Enumerating objects: 24, done.
remote: Counting objects: 100% (24/24), done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 24 (delta 0), reused 24 (delta 0), pack-reused 0
Receiving objects: 100% (24/24), 59.94 KiB | 1.28 MiB/s, done.
~ $ 
~ $ 
~ $ cd issue12451
issue12451 (main)$ 
issue12451 (main)$ 
issue12451 (main)$ ./gradlew run

> Task :app:run
JavaHelper() sees 0
GroovyHelper() sees 123

BUILD SUCCESSFUL in 5s
2 actionable tasks: 2 executed

osscontributor avatar May 17 '22 13:05 osscontributor

See https://issues.apache.org/jira/browse/GROOVY-10611 TL;DR: will be fixed in 3.0.11 when we release in a week or two. With 3.0.11, the stub will contain 123 as the constant value rather than 0. The other workaround for 3.0.10 is to use "123L" as the constant.

paulk-asert avatar May 19 '22 00:05 paulk-asert

Groovy version 3.0.11 has been released.

https://github.com/apache/groovy/releases/tag/GROOVY_3_0_11

fernando88to avatar May 31 '22 19:05 fernando88to

Groovy version 3.0.11 has been released.

@fernando88to FYI... At the moment is not yet available at https://repo.maven.apache.org/maven2/org/codehaus/groovy/groovy-all/.

osscontributor avatar May 31 '22 20:05 osscontributor

Apache has a 72 hr voting window on releases (it promotes human checking and approval over and above any earlier CI checks). The Tag is created at the start of that window but represents a yet unapproved release at that point. There is about 6 hrs left on that 72 hr window and then it will take a few hours after that to do the release and for it to sync up with Maven Central.

paulk-asert avatar May 31 '22 21:05 paulk-asert

I love it!❤️ Great work, everyone😎

torbjorn-boost avatar Jun 01 '22 10:06 torbjorn-boost