Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » Adapters(how to find out what drops them)
Adapters [message #1745120] Wed, 05 October 2016 11:46 Go to next message
Ludwig Moser is currently offline Ludwig MoserFriend
Messages: 476
Registered: July 2009
Senior Member
Hello,

First of all - i hope this is a "simple" questions, therefore i do not provide a small example program - which i can not provide as the 'problem' is reproduceable but not in an standalone app.

Description of my problem:
I have a case, where a new EObject gets created and an EAdapterImpl gets added to its eAdapters (lets call it 'ExampleAdapter' this adapterImpl supervises any changes on the newly created EObject and sends database queries)
many other adapters get added to this EObject and get removed while the application is running, which get removed too.

"Sometimes" the ExampleAdapter gets 'dropped'
i call it lost because i do not get any notifications about it!

i Override the method unsetTarget:
public void unsetTarget(Notifier oldTarget){
		Thread.dumpStack();
		System.exit(666);
	}

so all this does is printing the stack so i can see where the call is coming from then exits the application so no more logs get out.
this is all fine and nice (i tested it by calling the unsetTarget in my app)

i noticed that calling
EAdapterImpl_instance.unsetTarget(obj)
is equal to
obj.eAdapters().remove(EAdapterImpl_instance)

so my code above should pop up every time the ExampleAdapter gets removed.
fact is, that this simply does not happen.
the ExampleAdapter gets dropped from the EObject without notification

Question:
How can this happen at all? Is there a way to prevent this - or at least how i can find out where it happens?
Note: i can modify the code ExampleAdapter and the EObjects in any way needed.

PS: i already verified that the EObject i monitor is the same (so i do not create one and check another one later - this was the first thing i did Wink )
Even though this 'strange behaviour' can be caused by my code i have no clue how i could achieve that.
Re: Adapters [message #1745123 is a reply to message #1745120] Wed, 05 October 2016 12:32 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33137
Registered: July 2009
Senior Member
As you say, I cannot reproduce this with a test case like this:

package test;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcoreFactory;

public class Test
{
  public static void main(String[] args)
  {
    AdapterImpl adapter = new AdapterImpl()
      {
        @Override
        public void unsetTarget(Notifier oldTarget)
        {
          System.err.println("Unsetting");
        }
        
        @Override
        public void notifyChanged(Notification msg)
        {
          System.err.println("notifyChanged " + msg);
        }
      };

    EObject eObject = EcoreFactory.eINSTANCE.createEObject();
    eObject.eAdapters().add(adapter);
    eObject.eAdapters().remove(adapter);
  }
}

Note that the adapter itself receives a notification with it is removed.

How do you know the adapter is removed?


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Adapters [message #1745180 is a reply to message #1745123] Thu, 06 October 2016 06:33 Go to previous messageGo to next message
Ludwig Moser is currently offline Ludwig MoserFriend
Messages: 476
Registered: July 2009
Senior Member
My ExampleAdapter sends a QueryByExample to my database. Results are sent to Ui.
I noticed that - sometimes - there are no more results returning. I investigated that and noticed that the ExampleAdapter gets dropped.
My problem is that i was not able to find out where & when that happens.

This is why i ask this question here - is it possible to remove an Adapter from an EObject without getting an Notification - as this happens to me?
I thought this is not possible.
Re: Adapters [message #1745197 is a reply to message #1745180] Thu, 06 October 2016 11:38 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33137
Registered: July 2009
Senior Member
Certainly it's possible to call org.eclipse.emf.common.notify.Notifier.eSetDeliver(boolean) with false and then adapters will not be notified. Assuming your using MinimalEObjectImpl you could set a conditional breakpoint on org.eclipse.emf.ecore.impl.MinimalEObjectImpl.eBasicSetAdapterArray(Adapter[]) and condition it so that it only stops for that one EObject; I often use this.toString().contains(...) to test for something that makes the interesting object(s) different from all the rest.

Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Adapters [message #1745265 is a reply to message #1745197] Fri, 07 October 2016 06:54 Go to previous messageGo to next message
Ludwig Moser is currently offline Ludwig MoserFriend
Messages: 476
Registered: July 2009
Senior Member
just to be sure i searched all my java files for eSetDeliver and eDeliver
there is no useage of those methods so it should be impossible to remove an Adapter from an EObjectImpl without getting notified - right?

this is really strange, as other adapters (which stick to the same object) do not get dropped) - this is why its so strange. it would make more sense if all adapters on an object get dropped. but it only drops this special adapter

this is my ExampleSearchAdapter
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;

import lumo.core.biooffice.GUID;
import lumo.core.util.Services;
import lumo.ecore.emf.enums.DataType;
import lumo.swt.widget.form.internal.delay.DelayManager;

public class ExampleSearchAdapter<E extends GUID> extends AdapterImpl {
	private DelayManager delayManager;
	private int delay = 500;// ms
	private E element;
	
	public void unsetTarget(Notifier oldTarget){
		System.out.println("SOMETHING REMOVED THIS ADAPTER"); // never shown
	}

	private void delayQBE(final E e) {
		this.element = e;
		if (delayManager == null) {
			delayManager = new DelayManager("QueryByExample(" + element.getClass().getSimpleName() + ")", delay) {
				@Override
				public void doItNow() {
					try {
						DataType dataType = DataType.fromInstance(element);
						if (qbeIsEnabled() && Services.getController().containsNewElement(dataType) != null) {
							// the element did not get removed in the meantime
							try {
								Services.getPersistence().getSearchableStorage(dataType).queryByExample(element,
										true);
							} catch (Exception e2) {
								e2.printStackTrace();
							}
						}
						delayManager.stop();
						delayManager = null;
					} catch (Exception e2) {
						e2.printStackTrace();
					}
				}
			};
		} else {
			delayManager.postpone();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void notifyChanged(Notification notification) {
		if (notification.getEventType()==Notification.REMOVING_ADAPTER) {
			System.out.println("ExampleSearchAdapter got removed"); // never shown
		}
		E element = (E) notification.getNotifier();
		DataType dataType = DataType.fromInstance(element);
		if (debug)
			System.out.println(String.format("<EXAMPLE %s ADAPTER>", dataType.name().toUpperCase()));

		if (!element.isTransientDS() || element.isMarkedForDelete()) {
			// this would the ONLY legal way to remove the adapter but still...
			// unsetTarget is NEVER called
			// its no more transient -> remove this adapter
			//	this.unsetTarget(element); // same as next line
			element.eAdapters().remove(this);
			return;
		}
		
		// add QBE iff its enabled
		delayQBE(element);
	}
	
	private boolean qbeIsEnabled() {
		return true;
	}
}


DataType - holds information about the passed DataType
DelayManager - delays a QueryByExample until no new changes are reported within delay (500ms)
GUID - is an EClass which includes definition about ID and GUID for database only
Services.getPersistence().getSearchableStorage(dataType).queryByExample(element, true); - this only sends the query to my database service

as long as the Adapter does not get dropped its working fine Wink
Re: Adapters [message #1745278 is a reply to message #1745265] Fri, 07 October 2016 08:25 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33137
Registered: July 2009
Senior Member
Did you try what I suggested? Are you sure the adapter is removed, or you just assume this based on the symptoms? Are there multiple threads involved in adding and removing adapters. It's not a thread safe operation to add or remove and adapter...

Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Adapters [message #1745388 is a reply to message #1745278] Mon, 10 October 2016 05:43 Go to previous messageGo to next message
Ludwig Moser is currently offline Ludwig MoserFriend
Messages: 476
Registered: July 2009
Senior Member
sorry for the late reply (weekend)
i tried to override eSetDeliver and eBasicSetAdapterArray on my root element, triggering an System.exit which never happens.
i am sure the adapter is dropped from my current eobject as i output all adapters which are on my current EObject to console - others show up, this one does at first bit after dropped it does no more (obviously).
only one thread is adding and removing Adapters. Especially this Adapter should only get removed if my EObject gets saved by the user - save method is not called so nothing should remove the Adapter.
NOTE: i also removed the line of code where the Adapter gets removed (in save()) which remains in the same problem.
the symptoms only brought me to the point to check if the adapter is still in place - actually this was my last guess - i searched my code a lot for errors.

i check if the adapter is still in place with this code:
	
	public static void printEAdapters(EObject o) {
		if (o != null) {
			List<Adapter> adapters = o.eAdapters();
			System.out.println("EAdapters for " + o);
			for (Adapter adapter : adapters) {
				System.out.println(String.format("\t %s", adapter));
			}
		}
	}


and the check if it is still there:
	public static boolean hasExampleSearchAdapters(EObject o) {
		if (o != null) {
			List<Adapter> adapters = o.eAdapters();
			for (Adapter adapter : adapters) {
				if (adapter instanceof ExampleSearchAdapter) {
					return true;
				}
			}
		}
		return false;
	}


can an adapter be on an EObject without being listed in eAdapters()?

[Updated on: Mon, 10 October 2016 06:03]

Report message to a moderator

Re: Adapters [message #1745398 is a reply to message #1745388] Mon, 10 October 2016 10:33 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33137
Registered: July 2009
Senior Member
I assume you're using MinimalEObjectImpl as the base, but you haven't confirmed. I'd suggest the following. Modify the toString of your XyzImpl to return some unique string "MySpecialObject". Set a conditional breakpoint in org.eclipse.emf.ecore.impl.MinimalEObjectImpl.eBasicSetAdapterArray(Adapter[]) with condition this.toString().equals("MySpecialObject"). Run the application. Now you should stop at all places that modify the adapters associated with your special object. Alternatively, override that method in your XyzImpl, calling super, and set a breakpoint in your method at the place you're calling super. Of course the basic idea is to find out all places in the code that modify the adapter list of the object with which you're having problems.

Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Adapters [message #1745455 is a reply to message #1745398] Tue, 11 October 2016 07:21 Go to previous messageGo to next message
Ludwig Moser is currently offline Ludwig MoserFriend
Messages: 476
Registered: July 2009
Senior Member
sorry, i forgot to mention that my root element extends EObjectImpl.
i changed my root now (for testing) to extend MinimalEObjectImpl.
on my root element i override eSetDeliver and eBasicSetAdapterArray.
in eBasicSetAdapterArray i check
if the existing Adapter[] contains my ExampleAdapter and after set i check again. if its gone i exit my application.
there is no case where the application exits (i really expected it to close down)

	protected void eBasicSetAdapterArray(Adapter[] eAdapters) {
		boolean hadExampleSearchAdapter = EDetailTracker.hasExampleSearchAdapters(this);
		super.eBasicSetAdapterArray(eAdapters);
		if (this instanceof Contact && this.isTransientDS()) {
			// someone removed the examplesearchadapter
			if (hadExampleSearchAdapter && !EDetailTracker.hasExampleSearchAdapters(this)){
				Thread.dumpStack();
				System.exit(666);
			}
		}
	}

Re: Adapters [message #1745456 is a reply to message #1745388] Tue, 11 October 2016 07:46 Go to previous messageGo to next message
Felix Dorner is currently offline Felix DornerFriend
Messages: 392
Registered: December 2015
Senior Member
Ludwig Moser wrote on Mon, 10 October 2016 05:43

only one thread is adding and removing Adapters.

I think it's not just about write operations, but read operations may also cause harm here. IIRC the adapter list is initialized lazily so there may actually be an initialization error when multiple threads read the model at the same time. Make sure you're safe here too.
Re: Adapters [message #1745489 is a reply to message #1745456] Tue, 11 October 2016 13:40 Go to previous message
Ed Merks is currently offline Ed MerksFriend
Messages: 33137
Registered: July 2009
Senior Member
Felix,

The implementation for MinimalEObjectImpl's eAdapter's list is tricky. A new list is created for each call to eAdapters and that list is backed by an array kept in the eStorage. The array that it uses is never modified. In fact the array instance may be shared among many EObjects, so for example, when adding an EContentAdapter, all objects in the tree might well share the same array of adapters containing that EContentAdapter. So the list will certainly never see a partially initialized array...

Ludwig,

I really did suggest setting a breakpoint. It's best to find out where exactly are all the calls that modify the adapters on that object. If it's being removed or otherwise going missing, which could happen if multiple threads are adding adapters at the same time for the same object, all cases must set a new array and must go through this method. So either the adapter isn't really going missing or some call to this method sets an array that doesn't have this adapter in it. You've instrumented the call with logic, but I have no idea what that logic really tests and no idea if the logic itself is correct. Set a breakpoint to find out all places that modify the array. If there are multiple threads doing it, you should end up seeing the process stop in multiple threads.


Ed Merks
Professional Support: https://www.macromodeling.com/
Previous Topic:[CDO] Case in schema name with PostgreSQL
Next Topic:Model: abstract or interface?
Goto Forum:
  


Current Time: Fri Apr 19 13:07:08 GMT 2024

Powered by FUDForum. Page generated in 0.23388 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top