DataNucleus - Products
  History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: ENHANCER-96
Type: Bug Bug
Status: Resolved Resolved
Resolution: Won't Fix
Priority: Minor Minor
Assignee: Unassigned
Reporter: Chris Beams
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
JPOX Enhancer

Field initialisation not set in generated default constructor

Created: 14/Jul/07 02:01 AM   Updated: 15/Jul/08 08:20 AM
Component/s: None
Affects Version/s: 1.2.0-beta-3
Fix Version/s: None

File Attachments: 1. Java Source File Person.java (0.5 kb)
2. File Person.java.enhanced (8 kb)

Environment:
java version "1.5.0_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164)
Java HotSpot(TM) Client VM (build 1.5.0_07-87, mixed mode, sharing)

Mac OS X 10.4.9

Datastore: HSQL


 Description  « Hide

In classes that define one or more constructors, but do NOT explicitly define a no-arg constructor, transient fields that have a default value do not get populated by JPOX upon object reconstitution.

I've attached both the original and enhanced (decompiled) versions of Person.java

In the original, you can see there is just one constructor that takes reads as follows:

package example;

import javax.jdo.annotations.*;


@PersistenceCapable
public class Person {
    @Field
    private final String name;

    @Transient
    private String currentLocation = "nowhere";

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String getCurrentLocation() {
        return currentLocation;
    }

    public void setCurrentLocation(String currentLocation) {
        this.currentLocation = currentLocation;
    }
}


After enhancement, two constructors are present in the class:


package decompiled;

import javax.jdo.PersistenceManager;
import javax.jdo.spi.*;

public class Person implements PersistenceCapable {

    private final String name;
    private String currentLocation;

    public Person() {}

    public Person(String s) {
        currentLocation = "nowhere";
        name = s;
    }

    // [...]

}


EXPECTED BEHAVIOR: The no-arg constructor should initialize currentLocation = "nowhere" just as the String constructor does.
ACTUAL BEHAVIOR: The transient field is not initialized, resulting in a null value (and thus, NullPointerExceptions) to clients of the class.

Also note that if I remove the String constructor from the original Person class, the enhanced version's no-arg constructor DOES properly initialize the transient field. Also, if I explicitly add an empty no-arg constructor to the original Person class, again enhancement works as expected, initializing the transient field. It's only in the case that Person defines one or more non-no-arg constructors that this problem arises.

Additionally, I tried marking this field as transient in the following several ways. None of them had any effect; the bug still manifested in each case.

    @Transient
    private String currentLocation = "nowhere";

    private transient String currentLocation = "nowhere";

    @Field(persistenceModifier = NONE)
    private String currentLocation = "nowhere"


WORKAROUND:

The workaround for this bug is to explicitly declare a no-arg constructor in any classes that are affected (i.e.: classes that define one or more non-no-arg constructors AND do not define a no-arg constructor AND define transient fields with default values)

 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Chris Beams - 14/Jul/07 02:02 AM
Attaching original Person.java

Chris Beams - 14/Jul/07 02:02 AM
Attaching enhanced (decompiled) Person.java

Chris Beams - 14/Jul/07 02:03 AM
This behavior was tested against both 1.2.0-beta-2 and 1.2.0-beta-3 of the enhancer.

Andy Jefferson - 14/Jul/07 07:04 AM
Is this only with annotations OR the same with XML MetaData?
Which enhancer ? ASM, BCEL, both ?

Chris Beams - 14/Jul/07 03:41 PM
I was originally using BCEL. I just tried it with ASM and got the same results (slightly different class definition after decompilation, but the no-arg constructor still does not initialize the transient fields properly).

I'll try with XML metadata and get back to you.

Chris Beams - 15/Jul/07 05:44 AM
Just tried this with the following XML metadata (instead of annotations):

<jdo>
    <package name="example">
        <class name="Person" detachable="true">
            <field name="currentLocation" persistence-modifier="none"/>>
        </class>
    </package>
</jdo>

It produced the same result - the no-arg constructor fails to initialize the transient 'currentLocation' field with its default value.


So, to summarize:

ASM or BCEL enhancments, annotations or XML metadata, this bug appears to be pervasive.

Andy Jefferson - 15/Jul/07 10:56 AM
The issue is NOT specific to transient fields. It is initialisation of any field. If a user defines a field as

String myField = "someValue";

when the bytecode is read this initialisation is moved (by the Java compiler) to the available constructor(s). Since JPOX is trying to help the user do their JDO job (add in a default constructor where not provided it) it somehow needs to obtain this initialisation bytecode.

Andy Jefferson - 20/Aug/07 11:39 AM
It is not possible, in my investigations (consulting with the ASM group), to split out the initialisation of fields from static initialisation from user constructor code. This means that being able to replicate the initialisation of fields in the generated default constructor is not readily possible. Since the addition of default constructor by the enhancer is a nice-to-have feature and that it works for all but this I'm downgrading it to Minor due to the difficulty in providing it

Chris Beams - 21/Aug/07 03:28 AM
Thanks for the update.

Andy Jefferson - 15/Jul/08 08:20 AM
Not possible/practical