Bug 445663 - [Model] React to changed application model data for part data
Summary: [Model] React to changed application model data for part data
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.5   Edit
Hardware: PC All
: P3 major (vote)
Target Milestone: 4.5 M4   Edit
Assignee: Lars Vogel CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 405296 430988
  Show dependency tree
 
Reported: 2014-10-01 09:41 EDT by Lars Vogel CLA
Modified: 2014-11-06 07:22 EST (History)
8 users (show)

See Also:


Attachments
Screenshot (33.18 KB, image/png)
2014-10-10 04:37 EDT, Lars Vogel CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Lars Vogel CLA 2014-10-01 09:41:46 EDT
With the "model spy" from the e4 tools project I see in my IDE that I have lots of dead and duplicate PartDescriptors entries in my application model.

A dead PartDescriptor to me is a PartDescriptors of a part for which the class is not available anymore. This seem to happens if you deinstall a plug-in.

A duplicated PartDescriptor can be added by a incorrectly programmed model processor or model add-on.

I think the Eclipse IDE should add some "clean-up" code for incorrect model data. 

As a first step I suggest to write a model add-on which register itself for the application startup completed event and checks if identical part descriptors or dead part descriptors exists.

Opinions?
Comment 1 Dani Megert CLA 2014-10-03 09:32:21 EDT
(In reply to Lars Vogel from comment #0)
> With the "model spy" from the e4 tools project I see in my IDE that I have
> lots of dead and duplicate PartDescriptors entries in my application model.

Lars, do you have any steps that show the 'major' issue and allow us to claim victory once done?
Comment 2 Lars Vogel CLA 2014-10-06 18:31:13 EDT
(In reply to Dani Megert from comment #1)
> (In reply to Lars Vogel from comment #0)
> > With the "model spy" from the e4 tools project I see in my IDE that I have
> > lots of dead and duplicate PartDescriptors entries in my application model.
> 
> Lars, do you have any steps that show the 'major' issue and allow us to
> claim victory once done?

1.) Install the e4 tools, either from its official update site or from http://download.vogella.com/luna/e4tools (p2 update site)
2.) Use Ctrl +3 and open model spy
3.) Review the entries for Part Descriptors
4.) Deinstall a plug-in which contributes a view
5.) Open the model spy again and see that the entries are still part of the application model

Work related to Bug 376486 would also reveal broken data in the application model. For example the work in Bug 430988.
Comment 3 Lars Vogel CLA 2014-10-06 19:19:16 EDT
I have the BundleSymbolicName and the fully qualified class name. Example:

bundleclass://com.example.e4.rcp.todo/com.example.e4.rcp.todo.parts.DynamicPart2

Tom, can you advice what is the best way to check if a bundlesclass defined in the application model is present in the current OSGi runtime?
Comment 4 Thomas Watson CLA 2014-10-07 08:54:12 EDT
(In reply to Lars Vogel from comment #3)
> I have the BundleSymbolicName and the fully qualified class name. Example:
> 
> bundleclass://com.example.e4.rcp.todo/com.example.e4.rcp.todo.parts.
> DynamicPart2
> 
> Tom, can you advice what is the best way to check if a bundlesclass defined
> in the application model is present in the current OSGi runtime?

Something like this:

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;

...

public BundleCapability findPackage(final String packageName, String bundleSymbolicName, BundleContext bundleContext) {
	Requirement req = new Requirement() {
		@Override
		public Resource getResource() {
			// no resource
			return null;
		}
		@Override
		public String getNamespace() {
			return PackageNamespace.PACKAGE_NAMESPACE;
		}
		@Override
		public Map<String, String> getDirectives() {
			return Collections.singletonMap(
				PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
				"(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + packageName + ")");
		}

		@Override
		public Map<String, Object> getAttributes() {
			return Collections.emptyMap();
		}
	};
	Collection<BundleCapability> packages =
		bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).adapt(FrameworkWiring.class).findProviders(req);
		for (BundleCapability pkg : packages) {
			if (bundleSymbolicName.equals(pkg.getRevision().getSymbolicName())) {
			return pkg;
		}
	}
	return null;
}

public Class<?> findClass(String className, BundleCapability pkg) {
	BundleRevision revision = pkg.getRevision();
	BundleWiring wiring = revision.getWiring();
	if (wiring == null) {
		return null;
	}
	if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
		// fragment case; need to get the host wiring
		wiring = wiring.getRequiredWires(HostNamespace.HOST_NAMESPACE).get(0).getProviderWiring();
	}
	try {
		return wiring.getClassLoader().loadClass(className);
	} catch (ClassNotFoundException e) {
		return null;
	}
}
Comment 5 Lars Vogel CLA 2014-10-10 04:37:45 EDT
Created attachment 247791 [details]
Screenshot

We may also want to find model elements in the current UI which cannot get instantiated anymore. See attached screenshot.
Comment 6 Lars Vogel CLA 2014-10-10 07:38:40 EDT
(In reply to Lars Vogel from comment #5)
 
> We may also want to find model elements in the current UI which cannot get
> instantiated anymore. See attached screenshot.

From a discussion in platform.ui we should not handle this case in this bug. This bug should focus on bogus Part Descriptor data.
Comment 7 Markus Keller CLA 2014-10-10 07:54:57 EDT
Bug 405296 and dependencies show other problems with things that stay in the model for too long.
Comment 8 Simon Scholz CLA 2014-10-10 13:12:37 EDT
(In reply to Thomas Watson from comment #4)
> (In reply to Lars Vogel from comment #3)
> > I have the BundleSymbolicName and the fully qualified class name. Example:
> > 
> > bundleclass://com.example.e4.rcp.todo/com.example.e4.rcp.todo.parts.
> > DynamicPart2
> > 
> > Tom, can you advice what is the best way to check if a bundlesclass defined
> > in the application model is present in the current OSGi runtime?
> 
> Something like this:
> 
> import java.util.Collection;
> import java.util.Collections;
> import java.util.Map;
> 
> import org.osgi.framework.BundleActivator;
> import org.osgi.framework.BundleContext;
> import org.osgi.framework.Constants;
> import org.osgi.framework.namespace.HostNamespace;
> import org.osgi.framework.namespace.PackageNamespace;
> import org.osgi.framework.wiring.BundleCapability;
> import org.osgi.framework.wiring.BundleRevision;
> import org.osgi.framework.wiring.BundleWiring;
> import org.osgi.framework.wiring.FrameworkWiring;
> import org.osgi.resource.Requirement;
> import org.osgi.resource.Resource;
> 
> ...
> 
> public BundleCapability findPackage(final String packageName, String
> bundleSymbolicName, BundleContext bundleContext) {
> 	Requirement req = new Requirement() {
> 		@Override
> 		public Resource getResource() {
> 			// no resource
> 			return null;
> 		}
> 		@Override
> 		public String getNamespace() {
> 			return PackageNamespace.PACKAGE_NAMESPACE;
> 		}
> 		@Override
> 		public Map<String, String> getDirectives() {
> 			return Collections.singletonMap(
> 				PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
> 				"(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + packageName + ")");
> 		}
> 
> 		@Override
> 		public Map<String, Object> getAttributes() {
> 			return Collections.emptyMap();
> 		}
> 	};
> 	Collection<BundleCapability> packages =
> 	
> bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).
> adapt(FrameworkWiring.class).findProviders(req);
> 		for (BundleCapability pkg : packages) {
> 			if (bundleSymbolicName.equals(pkg.getRevision().getSymbolicName())) {
> 			return pkg;
> 		}
> 	}
> 	return null;
> }
> 
> public Class<?> findClass(String className, BundleCapability pkg) {
> 	BundleRevision revision = pkg.getRevision();
> 	BundleWiring wiring = revision.getWiring();
> 	if (wiring == null) {
> 		return null;
> 	}
> 	if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
> 		// fragment case; need to get the host wiring
> 		wiring =
> wiring.getRequiredWires(HostNamespace.HOST_NAMESPACE).get(0).
> getProviderWiring();
> 	}
> 	try {
> 		return wiring.getClassLoader().loadClass(className);
> 	} catch (ClassNotFoundException e) {
> 		return null;
> 	}
> }

Thank you for this sample code.

I just uploaded a ModelCleanupAddon as sample here:
https://git.eclipse.org/r/#/c/34735/

Unfortunately I have some inconsistency by using the findPackage(...) method.
In most cases it finds the package and I am also able to check, whether the class is available or not. But in some cases the package is not found even though the bundle is available.

I tested my Addon by starting with different run configurations, where I added or removed for example spy projects like org.eclipse.e4.tools.css.spy, org.eclipse.e4.tools.event.spy and so on. 

In case of the org.eclipse.e4.tools.css.spy bundle the package is never found even if the bundle is available and therefore should be found.
Other tested bundles worked as expected, which means that the package is found, when the bundle is available and not found, if the bundle is not available.

Can anyone image why the package of the org.eclipse.e4.tools.css.spy bundle is never found?
Comment 9 Thomas Watson CLA 2014-10-10 13:20:27 EDT
(In reply to Simon Scholz from comment #8)
> Can anyone image why the package of the org.eclipse.e4.tools.css.spy bundle
> is never found?

Is the package you are looking for exported?  I just realized that the package providing the function may not actually be API that should be exported.  Is that correct?
Comment 10 Thomas Watson CLA 2014-10-10 13:34:57 EDT
Assuming theses are internal packages (not exported) you would need to find the wirings based on bundle symbolic name and then use the wiring to find the class:

public Collection<BundleWiring> findWirings(final String bundleSymbolicName, BundleContext bundleContext) {
	Requirement req = new Requirement() {
		@Override
		public Resource getResource() {
			// no resource
			return null;
		}
		
		@Override
		public String getNamespace() {
			return IdentityNamespace.IDENTITY_NAMESPACE;
		}
	
		@Override
		public Map<String, String> getDirectives() {
			return Collections.singletonMap(IdentityNamespace.REQUIREMENT_FILTER_DIRECTIVE, "(" + IdentityNamespace.IDENTITY_NAMESPACE + "=" + bundleSymbolicName + ")");
		}
		
		@Override
		public Map<String, Object> getAttributes() {
			return Collections.emptyMap();
		}
	};
	Collection<BundleCapability> identities = bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).adapt(FrameworkWiring.class).findProviders(req);
	Collection<BundleWiring> result = new ArrayList<>(1); // normally only one
	for (BundleCapability identity: identities) {
		BundleRevision revision = identity.getRevision();
		BundleWiring wiring = revision.getWiring();
		if (wiring != null) {
			if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
				// fragment case; need to get the host wiring
				wiring = wiring.getRequiredWires(HostNamespace.HOST_NAMESPACE).get(0).getProviderWiring();
			}
			result.add(wiring);
		}
	}
	return result;
}

If you require the contributing bundle to be a singleton then there will only be on wiring in the collection because BundleWiring can only exist for resolved bundles and only one version of a singleton bundle is allowed to be resolved at a time.
Comment 11 Simon Scholz CLA 2014-10-10 13:46:19 EDT
(In reply to Thomas Watson from comment #10)
> Assuming theses are internal packages (not exported) you would need to find
> the wirings based on bundle symbolic name and then use the wiring to find

You are right, the package of the css parts are not exported, while the others are exported.

I will try the findWirings methods in a minute.
Thank you very much for your support Thomas.
Comment 12 Simon Scholz CLA 2014-10-10 14:49:31 EDT
Here is my proposal for removing unavailable PartDescriptors:

https://git.eclipse.org/r/#/c/34735/

Your findWirings() method works out.
Thank you Thomas :)
Comment 13 Lars Vogel CLA 2014-11-04 14:07:42 EST
(In reply to Simon Scholz from comment #12)
> Here is my proposal for removing unavailable PartDescriptors:
> 
> https://git.eclipse.org/r/#/c/34735/
> 
> Your findWirings() method works out.
> Thank you Thomas :)

Thanks Simon and Thomas for the help and the Gerrit review, I committed a slightly modified version of it with http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=8531da8680d98833ecd96f1a6df6807f05ee6c0a

This Model Addon will not affect incorrectly Part Descriptors contributed via the on ViewRegistry class, which evaluates the view extension point and adds these Views to the application model. But it should remove Part Descriptors with invalid classes, these would be for example part descriptors contributed by plug-ins based on model fragment which have been uninstalled. This should allow us to start using the application model in the IDE, e.g. via Bug 405296 for the Show View menu.
Comment 14 Lars Vogel CLA 2014-11-04 14:10:00 EST
(In reply to Lars Vogel from comment #13)
This should allow us to start using the application model in
> the IDE, e.g. via Bug 405296 for the Show View menu.

Sorry wrong bug number, Bug 430988 is the correct one.
Comment 15 Markus Keller CLA 2014-11-05 09:20:07 EST
(In reply to Lars Vogel from comment #13)
> http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/
> ?id=8531da8680d98833ecd96f1a6df6807f05ee6c0a

Please include the bug number in the commit comment.


This change caused a test failure in N20141104-2000:

Wrong bundles loaded: - org.eclipse.ui.ide.application
at org.eclipse.jdt.text.tests.PluginsNotLoadedTest.testPluginsNotLoaded(PluginsNotLoadedTest.java:263)

The reason is this addition in org.eclipse.ui.ide.application's MANIFEST.MF:
Bundle-ActivationPolicy: lazy

Why is this necessary? Does the bundle really have to be activated, given that it doesn't even have a Bundle-Activator? If there's a technical reason, then we can adjust the regression test. Otherwise, I think we can save a few cycles here and remove the Bundle-ActivationPolicy again.
Comment 16 Lars Vogel CLA 2014-11-05 09:39:59 EST
.
Comment 17 Lars Vogel CLA 2014-11-05 09:50:36 EST
Thanks Markus for catching this, the lazy flag is not necessary for this scenario. Fixed with http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=145ead594f2240c8d3709251257f14a22fcfbb0f
Comment 18 Lars Vogel CLA 2014-11-05 18:02:40 EST
Removing the dependencies (405296 430988) to avoid lots of emails for affect people. Actually the Lazy Flag is necessary, as otherwise the BundleContext in line 152 is null

bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION)
				.adapt(FrameworkWiring.class).findProviders(req);

I set the flag again with http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=5bfde32f45d74d736b205fb1b2c9fdd45ac65742

Simon, can you please have a look at the failing test?
Comment 19 Lars Vogel CLA 2014-11-05 18:02:55 EST
.
Comment 21 Lars Vogel CLA 2014-11-06 05:37:11 EST
Thanks Markus. The fix for the test was embarrassing easy.
Comment 22 Markus Keller CLA 2014-11-06 07:22:57 EST
(In reply to Lars Vogel from comment #21)
> Thanks Markus. The fix for the test was embarrassing easy.

No problem. It's our test, so it's also our job to look at it and fix it. I just wasn't sure if bundle activation was really necessary, and I didn't want to just update the test without seeing if it can be avoided.