Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 367738

Summary: getOldValue call on Notification from CDO returns null as opposed to old value
Product: [Modeling] EMF Reporter: Dan Pollitt <dan.s.pollitt>
Component: cdo.coreAssignee: Eike Stepper <stepper>
Status: CLOSED DUPLICATE QA Contact:
Severity: normal    
Priority: P3 CC: chewbizz, christian.mohr, dan.s.pollitt, esteban.dugueperoux, stepper
Version: 4.6   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard:

Description Dan Pollitt CLA 2012-01-03 05:53:29 EST
Build Identifier: org.eclipse.emf.cdo version 4.0.1.v20110831-1303

I'm observing that when I add an Adapter to an object and then somewhere else change the change the value of a reference from one object to another, that the notification that comes to my adapter always has getOldValue() as null. Identifying the newValue and the notifier works fine.

Reproducible: Always

Steps to Reproduce:
1. Have a CDO enabled model with an object of some type A that has a reference to some type B.
2. Have an instance, a of type A that has a reference to an instance b of type B.
3. Retrieve a reference to the object "a" through a CDOView with a ChangeSubscriptionPolicy of CDOAdapterPolicy.ALL that was obtained through a CDOSession with a PassiveUpdateMode of PassiveUpdateMode.CHANGES.
4. Attach an Adapter to "a" to be notified of changes.
5. Open a CDOTransaction through the same CDOSession as in step 3. Obtain a reference to "a" and change the reference from "b" to "b2" (another instance of type B).
6. Observe that a Notification is received to the Adapter added in step 4, the Notifications newValue is correctly reported as b2, however the Notifications oldValue is null, it should be "b".
Comment 1 Dan Pollitt CLA 2012-01-03 05:57:26 EST
Just to include other comments from newsgroup discussion:

I am setting the reference in a CDOTransaction in the same VM. The adapters view has already loaded the object being adapted as well as the old and new reference objects.
..
I need the old value in order to perform some fine-grained updates on a JFace Viewer.

Also setting: CDOView.Options.setInvalidationPolicy(CDOInvalidationPolicy.RELAXED) didn't seem to alter the behaviour.
Comment 2 Eike Stepper CLA 2012-08-14 22:52:23 EDT
Moving all open issues to 4.2. Open bugs can be ported to 4.1 maintenance after they've been fixed in master.
Comment 3 Eike Stepper CLA 2012-11-01 11:23:47 EDT
No activity or ping here for almost a year. And not test case. Please reopen this bug if you feel a need.
Comment 4 Eike Stepper CLA 2014-02-09 04:11:32 EST
From the newsgroup:

having classes 'A' and 'B', with 'A.b' referencing/containing instances
of 'B', I stumbled over the following oddity when setting 'A.b' to null:

Other "local" views and transactions [from same CDOSession] do *not* get
notified when 'A.b' is set to 'null', but they get notified when the
reference is changed to another instance of 'B'.

In other clients ["remote"], changes are visible immediately --
regardless if 'A.b' is set to 'null' or another instance of 'B'.
(So the client which sets to 'null' does NOT reflect the changes, but
all other clients do so)


On the one hand I might have overseen something obvious to those who
know what they're doing, on the other hand I'm a bit confused why things
work for "remote" clients as expectedly.


In short:
  EMFObservables.observeValue((A) instance, (EReference) bReference);

(Value)ChangeListeners fire for local modifications != 'null', and all
remote modifications regardless of value.

=====

Looking deeper into the matter, something seems to be wrong with
'oldValue's of Notifications.

Btw, this is Eclipse 4.3.1 with CDO 4.2 20130918 on Linux.

As mentioned, my application (= e.g. bound Viewers..) does not react on
the "local" change of a reference to value 'null'. Viewers and listeners
react on changing references to *other* (non-null) values, and they
react on changes to value 'null' when the change was committed from
another session(sic).

Bug 367738, result of ~2 years old thread "[CDO]
Notification#getOldValue always null?", was apparently closed to sudden
lack of any interest.
[ https://bugs.eclipse.org/bugs/show_bug.cgi?id=367738 ]

According to that thread, the original poster encountered unexpected
'oldValue's valued 'null'.

My problem also seems to be related to getting unexpected 'oldValue's of
value 'null'".

My (local) Viewers do not react, because method "isTouch", in class
org.eclipse.emf.common.notify.impl.NotificationImpl for event-types
(non-primitive-)SET and UNSET returns:

 oldValue == null ? newValue == null : oldValue.equals(newValue)


In my case (local change), and --probably same issue as for the creator
of bug 367738--, 'oldValue' is (incorrectly? but always) null, so when
newValue [UNSET/SET value 'null'] is 'null', too - local Viewers will
not update.


--
For me personally I hacked kindof

 if type==UNSET then oldValue=new Object()

into org.eclipse.emf.internal.cdo.object.CDONotificationBuilder to fool
this rule, and .. I do get my updates.
--


I personally am too new to all this to dare to call this a bug, but when
changing a reference from "anything non-null" to "null", or when
unsetting a reference, oldValue should not be null.
Comment 5 Eike Stepper CLA 2014-02-10 13:03:02 EST
I've added a test case:

public class Bugzilla_367738_Test extends AbstractCDOTest
{
  public void testNotificationOldValue() throws Exception
  {
    Company company = getModel1Factory().createCompany();
    company.setName("ESC");

    CDOSession session = openSession();
    CDOTransaction transaction = session.openTransaction();
    CDOResource resource = transaction.createResource(getResourcePath("/test1"));
    resource.getContents().add(company);
    transaction.commit();

    TestAdapter adapter = new TestAdapter();
    company.eAdapters().add(adapter);

    company.setName(null);
    Notification[] notifications = adapter.getNotifications();
    assertEquals("ESC", notifications[0].getOldValue());
  }
}

It does not fail. It could be that I don't test for the right thing but I also vaguely remember that we did something with respect to oldValues in this current stream. Have you already tried your scenario with the latest CDO 4.3 builds?
Comment 6 Christian Mohr CLA 2014-05-15 12:57:22 EDT
Hi, we're having this problem too, and i tried to debug it. I'll try to describe the things i noticed. I hope they help.

1. In the CDONotificationBuilder.visit(CDOSetFeatureDelta delta) you are calling getOldValue(feature). There the oldRevision is null and null is returned. Next there is only a check if oldValue is a CDOID, but not if it is an attribute (e.g String). So the oldValue stays null. For our configuration, we have auditing/branching off and i used h2 for the tests.

A modified Version of your Test:

public void testOldValue_Attribute() throws Exception
  {
    CDOID id;
    Company company = getModel1Factory().createCompany();
    company.setName("ESC");

    CDOSession session = openSession();
    CDOTransaction transaction = session.openTransaction();
    CDOResource resource = transaction.createResource(getResourcePath("/test1"));
    resource.getContents().add(company);
    transaction.commit();
    id = CDOUtil.getCDOObject(company).cdoID();
    CDOView view = session.openView();
    Company companyView = (Company)view.getObject(id);

    TestAdapter adapter = new TestAdapter();
    companyView.eAdapters().add(adapter);

    company.setName(null);
    transaction.commit();
    sleep(10000);
    Notification[] notifications = adapter.getNotifications();
    Object oldValue = notifications[0].getOldValue();
    assertEquals("ESC", oldValue);
  }

2. Maybe the same problem is with CDONotificationBuilder.visit(CDORemoveFeatureDelta delta). Here its not the old value, but the detachedObjects (it contains references to "invalid" objects). Frameworks like Incquery expect a reference to the "invalid" removed Object, to determine which element was removed. If there is just one detachedObject it is possible to send it as oldValue, but with more than one there will have to be more than one notification. What do you think about that?


I hope you can use this information.
Comment 7 Esteban DUGUEPEROUX CLA 2014-07-15 05:17:17 EDT
I have executed the test of https://bugs.eclipse.org/bugs/show_bug.cgi?id=367738#c6 and indeed it fails with non auditing because when adding the new CDORevision of Company in CDORevisionCacheNonAuditing the addRevision() method call has the effect to remove the old CDORevision. Consequently in invalidation, i.e. CDOSessionImpl$Invalidation.reviseRevisions(), we cannot retreive the old CDORevision to get the old value.
This issue doesn't occurs between 2 differents CDOSession because the old CDORevision has not yet been removed on the remote CDOSession.

I have submit a Gerrit change-set with the test of previous comment : https://git.eclipse.org/r/#/c/29890/.

The fix needs to refactor the CDORevisionManager/CDORevisionCache to remove old CDORevision later.
Comment 8 Esteban DUGUEPEROUX CLA 2014-07-15 05:52:15 EDT
The Gerrit job which launch the tests suites is executed with auditing enabled, then the test does not failed. I have updated the change-set to add scenario with auditing disabled.
Comment 9 Eike Stepper CLA 2015-07-14 02:12:24 EDT
Moving all open bugzillas to 4.5.
Comment 10 Eike Stepper CLA 2016-07-31 00:55:02 EDT
Moving all unaddressed bugzillas to 4.6.
Comment 11 Eike Stepper CLA 2016-10-05 12:54:45 EDT
I think I fixed this in the context of bug 395685.

*** This bug has been marked as a duplicate of bug 395685 ***
Comment 12 Eike Stepper CLA 2020-12-11 10:25:06 EST
Closing.
Comment 13 Eike Stepper CLA 2020-12-11 10:39:41 EST
Closing.