Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

eclipselink static weaving with final fields on Java 9

I have some JPA annotated fields declared final like this:

@Column(name = "SOME_FIELD", updatable = false, nullable = false)
private final String someField;

Those fields are stored in the database when the entity is inserted in the database. They cannot further be updated. For the Java programming language, such fields can be considered final.

With the EclipseLink static weaving plugin, it's possible to lazy load entities, owing to some byte code instrumentation.

I don't know if such constructs (final fields) are officially allowed in JPA, but I like to use them, since they enforce at compile time that these fields are not meant to be updated.

In Java 8, programs built with such constructs run fine. But in Java 9, when the EclipseLink static weaving is involved, I get the following runtime exception:

Caused by: java.lang.IllegalAccessError: Update to non-static final field xxx.yyy.SomeEntity.someField attempted from a different method (_persistence_set) than the initializer method <init> 

Such an error seems to be due to the following JVM specification:

Otherwise, if the field is final, it must be declared in the current class, and the instruction must occur in an instance initialization method () of the current class. Otherwise, an IllegalAccessError is thrown.

Therefore, I feel like some weaving tools need some update to fulfill this JVM specification. The EclipseLink static weaving tool seems to be one of them.

Questions:

  • Are final field constructs such as those presented here allowed in JPA?
  • Is there a JDK 9 option to disable the check for final field assignment elsewhere than only in the instance initialization method() of the class (as a temporary workaround)?

Edit:

Final fields are not allowed in JPA, as per JPA specifications:

The entity class must not be final. No methods or persistent instance variables of the entity class may be final.

like image 888
Stéphane Appercel Avatar asked Sep 23 '17 04:09

Stéphane Appercel


2 Answers

Are final field constructs such as those presented here allowed in JPA?

As mentioned in the release note JDK-8157181 as well there is a change brought to restrict Compilers to accept modification of final fields outside initializer methods.


According to the Java VM Specification, the putstatic bytecode is allowed to modify a final field only

  1. if the field is declared in the current class (the class that declares the current method) and
  2. if the putstatic instruction appears in the class or interfaceinitializer method <clinit> of the current class.

Otherwise, an IllegalAccessError must be thrown.

Similarly, the putfield bytecode is allowed to modify a final field only

  1. if the field is declared in the current class and
  2. if the instruction appears in an instance initializer method of the current class.

Otherwise, an IllegalAccesError must be thrown.

Methods that do not satisfy condition (2) violate the assumptions of the compilers. and have been kept under a check to implement the desired condition with Java 9.

You can follow the BugReport over the same for a detailed explanation.


Is there a JDK 9 option to disable the check for final field assignment elsewhere than only in the instance initialization method() of the class (as a temporary workaround)?

With the JDK 9 release, the HotSpot VM fully enforces the previously mentioned restrictions, but only for class files with version number >= 53(Java 9). For class files with version numbers < 53, restrictions are only partially enforced (as it is done by releases preceding JDK 9). That is, for class files with version number < 53 final fields can be modified in any method of the class declaring the field (not only class/instance initializers).

So, you can try compiling your code with source and target 1.8 to check if that resolves the issue for now. This should be doable with --release 8 option using the latest javac tool.

like image 198
Naman Avatar answered Oct 18 '22 06:10

Naman


Please read JPA specification - http://download.oracle.com/otndocs/jcp/persistence-2_2-mrel-eval-spec/index.html - Section 2.1 The Entity Class:

The entity class must not be final. No methods or persistent instance variables of the entity class may be final.

Method _persistence_set is code added by weaving (bytecode manipulation of entity classes) and is used to initialize values of entity attributes after class was created by default constructor (with no attributes) call. Java 1.8 allowed this even with final fields, but Java 9 does not.

You should now follow JPA specification and should not put final persistence attributes into your entities.

like image 43
Tomas Kraus Avatar answered Oct 18 '22 07:10

Tomas Kraus