objectdb-2.6.9_02 (with "objectdb.temp.no-enhancement-crc-check" system property)
The following is a detailed report on investigation of multiple issues that have been mentioned in the following forum postings and issue reports (but please do not visit them from here now, the diagnostics there are now obsolete):
- Possible cause for "Enhancement of type ... is old and cannot be used"
This report concerns investigation of issues when using with generic classes for "deep" value wrappers with signatures like:
@Entity abstract public class Value<T> extends [SomeOtherEntity] implements IValue<T> { @Entity public class BooleanValue extends Value<Boolean> implements IBooleanValue { @Entity public class IntegerValue extends Value<Integer> ... { @Entity abstract public class Quantity<T extends Number> extends Value<T> implements IQuantity<T> { @Entity public class FloatQuantity extends Quantity<Float> implements IFloatQuantity { @Entity public class IntegerQuantity extends Quantity<Integer> implements IIntegerQuantity { @Entity public class StringValue extends Value<String> implements IStringValue { @Entity public class EnumValue<T extends Enum<T>> extends Value<T> {
It is important to note that Value<T> implements IValue<T>, as this has consequences for at least one issue (see below).
The above "deep" value wrapper classes are crucial to an Expert System web app I develop; please do not be further concerned with why one would bother to wrap something like a Boolean in this fashion, it is however a very powerful and crucial technique (and it is not used in all cases for every trivial boolean value in the system, just for Expert System cases).
To make things simpler, this posting will examine only these 2 cases:
@Entity public class BooleanValue extends Value<Boolean> implements IBooleanValue { @Entity public class EnumValue<T extends Enum<T>> extends Value<T> {
Note in particular the complex signature of the EnumValue compared with the other generic value wrapper examples, as it exhibits a problem with ObjectDB that the others do not.
Some important remarks about the analysis, diagnostics, and the testing setup:
- The diagnostics here have been recorded carefully at every step, including Git tracking and tracking in a CMS web system.
- I have used both a console test app and a NetBeans web app in Glassfish; the initial report is limited to the console app, as the web app introduces some other factors (especially concerning the Ant build).
- The NetBeans project for the console app uses a post-compile, pre-deployment Ant script invoked using Clean and Build that enhances every entity class in the system.
- Very important: I discovered to my horror (using Java decompilers and .class file time stamp checks) that when running my "console" test app in NetBeans IDE using the Run context menu it was in fact over-writing the enhanced .class files ! Therefore, some of my diagnostics in other recent postings are incorrect. This must be something to do with the Ant build.xml and build-impl.xml (a problem for another day).
- All reports here are from executing the console test app from a shell/terminal window, NOT from NetBeans (although the test app is developed in NetBeans and the Clean and Build with enhancement works fine).
- I have used Java decompilers to check the ObjectDB enhancement carefully. I recommend the JD-GUI decompiler, which has better support for generics than the now-obsolete JAD. You can also find out about other Java decompilers (including a cloud-based service - http://www.javadecompilers.com/). If you are new to ObjectDB I highly recommend that you inspect the decompiled enhanced output for entity classes.
- I have also used UNIX file timestamp checking to ensure enhancement from Clean and Build has not been corrupted.
- No javaagent enhancement is used at any stage (and timestamp checking of enhanced .class files indicates that no "automatic" enhancement has taken place).
- [EDIT: contrary to my original report, to prevent the persistence error one can explicitly set System.setProperty("objectdb.temp.no-enhancement-crc-check", "true"); in objectdb-2.6.9_02 because the default behaviour is "false"]
About Build 2.6.9_02 and the "objectdb.temp.no-enhancement-crc-check" system property
From ObjectDB support from 2016-08-19:
'Using generic entity classes with ObjectDB may work to some extent. You can use a generics class parameter as the type of a persistent field, but in that case ObjectDB will consider that persistent field type as java.lang.Object.
Build 2.6.9_02 supports an option to disable the "Enhancement ... is old" check and error:
System.setProperty("objectdb.temp.no-enhancement-crc-check", "true");
Set the property before accessing ObjectDB do disable ObjectDB checks. If all your classes are enhanced properly and the problem is in this check then you may be able to use your application with the last build.'
What my test app does:
- Creates a Project entity with a Model entity child which in turn has BooleanValue and EnumValue entity children.
- Uses Java Reflection to inspect all getters and setters (I am using property-based JPA) and all ObjectDB hidden methods (which are no longer getters/setters) and echos this information. This is also one way of checking the enhancement.
- Attempts to persist the Project (which under some circumstances gives errors).
This forum posting does NOT report on anything other than errors during persistence, that is no conclusions are drawn yet about whether ObjectDB can handle retrieval of information in such generic value wrapper classes.
Case1:
- System.setProperty("objectdb.temp.no-enhancement-crc-check", "false");
- EnumValue has the following methods that are NOT defined as operations in IValue<T>.
private T oldValue; public T getOldValue() { return oldValue; } public void setOldValue(T oldValue) { this.oldValue = oldValue; }
- BooleanValue has the equivalent, but with simpler generic resolution:
private Boolean oldValue; public Boolean getOldValue() { return oldValue; } public void setOldValue(Boolean oldValue) { this.oldValue = oldValue; }
Result: on persistence an error occurs:
Persist project.. Exception in thread "main" [ObjectDB 2.6.9_02] javax.persistence.RollbackException Failed to commit transaction: Failed to locate set method for field property com.greensoft.objectdb.test.entity.value.EnumValue.oldValue using reflection (error 613) at com.objectdb.jpa.EMImpl.commit(EMImpl.java:290) at com.greensoft.objectdb.test.console.Main.createProject(Main.java:129) at com.greensoft.objectdb.test.console.Main.run(Main.java:250) at com.greensoft.objectdb.test.console.Main.main(Main.java:274) Caused by: javax.persistence.PersistenceException: com.objectdb.o.UserException: Failed to locate set method for field property com.greensoft.objectdb.test.entity.value.EnumValue.oldValue using reflection at com.objectdb.o._PersistenceException.b(_PersistenceException.java:47) at com.objectdb.o.JPE.g(JPE.java:145) at com.objectdb.o.JPE.g(JPE.java:78) ... 6 more Caused by: com.objectdb.o.UserException: Failed to locate set method for field property com.greensoft.objectdb.test.entity.value.EnumValue.oldValue using reflection at com.objectdb.o.MSG.d(MSG.java:75) at com.objectdb.o.UMR.P(UMR.java:937) at com.objectdb.o.UMR$ac.i(UMR.java:1382) at com.objectdb.o.MMM.(MMM.java:299) at com.objectdb.o.UTY.X(UTY.java:620) at com.objectdb.o.TYS.y(TYS.java:787) at com.objectdb.o.TYS.x(TYS.java:639) at com.objectdb.o.TYS.v(TYS.java:591) at com.objectdb.o.TYM.ae(TYM.java:549) at com.objectdb.o.TYM.ac(TYM.java:485) at com.objectdb.o.TYM.ao(TYM.java:806) at com.objectdb.o.TYM.at(TYM.java:891) at com.objectdb.o.EPR.UC(EPR.java:77) at com.objectdb.o.CLT.visitRefs(CLT.java:161) at com.objectdb.o.TVS.j(TVS.java:169) at com.objectdb.o.TVS.cascade(TVS.java:156) at com.objectdb.o.STA.Q(STA.java:476) at com.objectdb.o.STM.E(STM.java:411) at com.objectdb.o.OBM.bP(OBM.java:931) at com.objectdb.jdo.PMImpl.bP(PMImpl.java:2279) at com.objectdb.o.OBM.bO(OBM.java:842) at com.objectdb.o.OBM.bM(OBM.java:751) at com.objectdb.jpa.EMImpl.commit(EMImpl.java:287) ... 3 more Caused by: java.lang.NoSuchMethodException: com.greensoft.objectdb.test.entity.value.EnumValue.setOldValue(java.lang.Object) at java.lang.Class.getDeclaredMethod(Class.java:2130) at com.objectdb.o.UMR$ac.i(UMR.java:1368) ... 23 more
Analysis: inspection of the decompiled enhanced classes shows there is no explicit setOldValue(java.lang.Object) method (there is only setOldValue(T)), but there are also no explicit calls to setOldValue(java.lang.Object) within the entity class file. I don't understand why the error occurs, but setOldValue(java.lang.Object) must be called from elsewhere.
The odb logs show:
[2016-08-24 16:14:34 #15 type.registry] New type com.greensoft.objectdb.test.entity.value.BooleanValue [2016-08-24 16:14:34 #16 type.user] Enhancement of type com.greensoft.objectdb.test.entity.value.BooleanValue is old and cannot be used (3308841709508783010:1622213375148932442) [2016-08-24 16:14:34 #17 type] Type com.greensoft.objectdb.test.entity.value.BooleanValue is not enhanced. [2016-08-24 16:14:34 #18 type.registry] New type com.greensoft.objectdb.test.entity.value.EnumValue [2016-08-24 16:14:34 #19 type.user] Enhancement of type com.greensoft.objectdb.test.entity.value.EnumValue is old and cannot be used (8514268054569000235:4597179754324543026) [2016-08-24 16:14:34 #20 type] Type com.greensoft.objectdb.test.entity.value.EnumValue is not enhanced.
The "fix": I discovered that if I include these in the IValue<T> interface the runtime error on attempting to persist disappears (but this does not "fix" the log enhancement report problem):
public interface IValue<T> { ... T getOldValue(); void setOldValue(T oldValue); ... }
Case2:
- System.setProperty("objectdb.temp.no-enhancement-crc-check", "false");
- Methods in EnumValue and BooleanValue defined as operations in interface IValue as shown above.
Result:
The odb logs show:
[2016-08-24 16:14:34 #15 type.registry] New type com.greensoft.objectdb.test.entity.value.BooleanValue [2016-08-24 16:14:34 #16 type.user] Enhancement of type com.greensoft.objectdb.test.entity.value.BooleanValue is old and cannot be used (3308841709508783010:1622213375148932442) [2016-08-24 16:14:34 #17 type] Type com.greensoft.objectdb.test.entity.value.BooleanValue is not enhanced. [2016-08-24 16:14:34 #18 type.registry] New type com.greensoft.objectdb.test.entity.value.EnumValue [2016-08-24 16:14:34 #19 type.user] Enhancement of type com.greensoft.objectdb.test.entity.value.EnumValue is old and cannot be used (8514268054569000235:4597179754324543026) [2016-08-24 16:14:34 #20 type] Type com.greensoft.objectdb.test.entity.value.EnumValue is not enhanced.
But, at least as far as persisting my model is concerned, this seem harmless.
In fact - although the log insists that BooleanValue and EnumValue are NOT enhanced - they are indeed enhanced (as one can prove using time-stamp checks and the decompiler), so the log message is inaccurate.
Case3:
- System.setProperty("objectdb.temp.no-enhancement-crc-check", "true"); //!
- Methods in EnumValue and BooleanValue defined as operations in interface IValue<T> as shown above.
Result: no error on attempting to persist the model, and no errors in the odb log.