Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "E4/Contexts"

< E4
(General Requirements)
Line 139: Line 139:
 
# Separation of usage from knowledge of environment to increase reuse (inversion of control)
 
# Separation of usage from knowledge of environment to increase reuse (inversion of control)
 
# Lookup of both simple variable values and "live" service objects
 
# Lookup of both simple variable values and "live" service objects
 +
# Flexible algorithm for service lookup - how a service is retrieved is completely in the hands of the local context, or perhaps some pluggable strategy inserted into the local context
 +
 +
= Interesting Use Cases =
 +
 +
The following are some interesting concrete use cases that came up during discussion

Revision as of 17:43, 9 January 2009

Summary

In the general, execution or evaluation contexts, that can provide information to the currently executing code. This document discusses the general problem, provides a survey of currently available technology in Eclipse, and establishes requirements for a common solution.

Problems

The following are some problems present in Eclipse 3.x that we are seeking to address:

  1. We have a large number of global singleton accessors (see Platform, PlatformUI, etc)
    • This tightly couples consumers of "things" to their producer/provider, inhibits reuse, etc
  2. Mechanisms are not very dynamic - react to context changes drastically rather than incrementally (just close the affected controls)
  3. IEvaluationContext has global state that gets swapped according to context change (such as the focus control)
  4. Current solutions are not multi-threadable - they assume evaluation occurs in the UI thread
  5. Scaling granularity
    • Current solutions don't scale down to services with brief lifetimes
    • Current solutions may not scale up to very large numbers of services
  6. There are too many parallel trees that mimic each other (widget tree, service locator tree, etc)
  7. Currently don't track service consumers - to notify when services come/go
  8. We expose containerisms to client code - they need to know where things come from
  9. No support for looking up services that are composed out of other services on the fly

Overview of current context mechanisms

IEquinoxContext (e4)

Bug 259423 Add notion of "context" that can be injected into objects

org.eclipse.core.runtime.RegistryContextFactory - Creates objects based on the registry's configuration element. Creates a user-specified class and injects into it contents of the configuration element and, optionally, the supplied context

org.eclipse.equinox.context.IEquinoxContext - This interface represents a hierarchical context with dependency injection capabilities. The context might have number of objects associated with it. For instance, a context might include log, status, and preferences. We'll call such associated objects "services" (through they don't have to be OSGi services).

An example use (from tests):

Integer testInt = new Integer(123);
String testString = new String("abc");
 
// create context
IEquinoxContext context = ContextFactory.createContext();
// elements to be populated via fields
context.addObject("busyCursorTimeout", testInt);
context.addObject("username", testString); // this checks capitalization as well
 
ObjectBasic userObject = new ObjectBasic();
context.injectInto(userObject);

IServiceLocator (3.x)

In Eclipse 3.x we have the notion of part hierarchy: Workbench, WorkbenchWindow, WorkbenchPart (Editor/View), and nested part (MultiPageEditorPart or PageBookView).

Bug 92646 comment #11 [Workbench] [RCP] Allow developers to register Workbench services

There is a description of our services/service locator hierarchy in comment #11, basically we use this model to support 3 things:

  • A service behaviour accessed through a local context (not PlatformUI :-)
  • Scoping of the service to the local context
  • Local service lifecycle, allowing the de-activation of contributions and listener clean up
IFooService fooService = (IFooService) getSite().getService(IFooService.class);
fooService.activateFoo("org.eclipse.ui.textScope");

Each service is hierarchical and mirrors the IServiceLocator hierarchy. Services are created lazily on the call to getService(Class). The lookup algorithm is:

  1. Check our cache to see if we already have an instance of the service
  2. Check our local org.eclipse.ui.services.AbstractServiceFactory to see if we can create a local override version
  3. Go to the org.eclipse.ui.services registry, org.eclipse.ui.internal.services.WorkbenchServiceRegistry to see if we can create the service
  4. Use the parent locator to try and look up the service

When creating a service, you have access to:

Object create(Class serviceInterface, IServiceLocator parentLocator, IServiceLocator locator);
  • serviceInterface: the service we need to create
  • parentLocator: A locator that can return a parent service instance if desired
  • locator: the service locator which can be used to retrieve dependent services

When we create our service locators using org.eclipse.ui.internal.services.IServiceLocatorCreator we pass in an owner (an IDisposable). If we are using services that go away (for example, the plugin is unloaded) we will call dispose() on the owner. For example, creating a service locator for a workbench window looks like:

serviceLocator = (ServiceLocator) slc
	.createServiceLocator(workbench, null, new IDisposable(){
		public void dispose() {
			final Shell shell = getShell();
			if (shell != null && !shell.isDisposed()) {
				close();
			}
		}
	});

IEvaluationContext (3.x)

Provided by org.eclipse.core.expressions this is the application context used in 3.x by the IEvaluationService (and hence all declarative expressions like enabledWhen/activeWhen/visibleWhen)

EvaluationContext is hierarchical and has a lookup strategy that checks the local cache and then asks the parent. In 3.x we create the root org.eclipse.core.expressions.EvaluationContext in our EvaluationAuthority(ExpressionAuthority) and populate it from the ISourceProviders, mostly contributed through org.eclipse.ui.services. In 3.x, the context hierarchy does not match the part hierarchy, it is basically flat. This supports the global application context used by expressions and the command framework, but prevents the notion of local context.

String hi = "hi";
EvaluationContext context = new EvaluationContext(null, hi);
context.addVariable("selection", hi);
//... not exactly a stellar example :-)
String selection = (String)context.getVariable("selection");

In e4

In E4 we're investigating a different part of the IEvaluationContext API, resolveVariable(*).

Object resolveVariable(String name, Object[] args);

Each EvaluationContext in the hierarchy can be provided one or more org.eclipse.core.expressions.IVariableResolvers. That allows the EvaluationContext to delegate the lookup. The algorithm is to iterate through the resolvers and return the first non-null answer, and if nothing is found delegate the resolve query to the parent.

In the e4 command investigation, Bug 257429 Command investigation phase 1, each workbench model Part<?> has an associated context. The IServiceLocator behaviour could be implemented by resolving a service variable "org.eclipse.e4.service" with the service as an argument.

IHandlerService hs = (IHandlerService) l.resolveVariable(IServiceLocator.SERVICE, new Object[] { IHandlerService.class });

The actual order of lookup for the context to execute a menu item is:

  1. Find the focus control
  2. Walk up the SWT parent hierarchy until we find a control that is owned by a Part<?> like a view or workbench window
  3. Go to the application and look up the IEvaluationContext that was created with that Part<?>

With this mapping, the IEvaluationContext and IServiceLocator hierarchy are identical, and extending this to support different kinds of lookups for different variables means adding more IVariableResolvers.

General Requirements

The following are some very general requirements of a context system:

  1. Support notion of service availability lifecycle. The service publisher can add or withdraw services at any time
  2. Support notion of service usage lifecycle. Some services may want to perform cleanup when clients are no longer using the service
  3. Eliminate global singletons
  4. Separation of usage from knowledge of environment to increase reuse (inversion of control)
  5. Lookup of both simple variable values and "live" service objects
  6. Flexible algorithm for service lookup - how a service is retrieved is completely in the hands of the local context, or perhaps some pluggable strategy inserted into the local context

Interesting Use Cases

The following are some interesting concrete use cases that came up during discussion

Back to the top