Table of Contents

Eclipse - Rich Client Platform (RCP)

Documents

Fallpits

Target Platform

<fc #FF0000>ACHTUNG</fc>: - When adding stuff from the platform
<fc #FF0000>Remove the Group by Category flag and add the following components.</fc>
Or some plugins will not be listed!!!!!!!

A custom Target Platform contains all the artifacts, which teh projects depends on. (libs)

IDE Independent Target Platform

Just copy the plugins dir form your IDE to a libs dir and point the target platform to this direction. This will protect the project from IDE changes and uncontrolled installation of incompatible plugins.

Sources of packages, bundles and libs

All p2 update sites are listed here: https://wiki.eclipse.org/Eclipse_Project_Update_Sites

SiteFeatureBundles

http://download.eclipse.org/eclipse/updates/4.5 <br> The swt packages are located inside the Eclipse RCP Feature.

Eclipse RCP
  • org.eclipse.swt
  • org.eclipse.jface
E4 Downloads - http://download.eclipse.org/e4/downloads/drops/S-0.17-201501051100/repository/
  • E4 CSS Spy
  • E4 Event Spy
  • Eclipse E4 Tools
Nattable 1.3.0 - http://download.eclipse.org/nattable/releases/1.3.0/repository/
  • NatTable Core
  • org.apache.commons.logging
EMF Common (remove Group by Category)
  • org.eclipse.e4.emf.xpath
EMF - Eclipse Modeling Framework Core Runtime (remove Group by Category)
  • org.eclipse.emf.ecore
Eclipse-Platform x.y
  • org.eclipse.ui.forms
Portability

To keep the libs locally in a project and to have it portable between different workstations (e.g. by keeping projects in dropbox)

The Eclipse Projects are usually kept on dropbox, but the workspace is located on the local machine, because it uses absolute paths. So the problem is, set the path to the libs workspace independent.

This can be achieved by using eclipse variables. But there is no special variable which always points to the targetPlatform location. (target_home var points to the first entry in target_platform)

The solution is to import the target_platform project into the workspace and use the variable, which contains an absolute path to the target_platform location. From there you can locate the subdirectories with libs.

<location path="${workspace_loc:/de.vogella.e4.rcp.target/libs}\nebula" type="Directory"/>
<location path="${workspace_loc:/de.vogella.e4.rcp.target/libs}\eclipseHomeLuna" type="Directory"/>
<location path="${workspace_loc:/de.vogella.e4.rcp.target/libs}\nattablerepo" type="Directory"/>
...

The Project de.vogella.e4.rcp.target is imported, and so it may be used to locate libs, which are located in subdirectories

Example Setup

Here is a setp of a target platform, which contains all the necessary plugins / features to run a product.

Tools

Tool Where Screenshot
Eclipse e4 Tools snag.gy_lqtak.jpg
CssSpy - Can inspect the application structure, display the css rules.
You should assign a hotkey to “open CSS Spy” action, e.g. ALT+SHIFT+F5

  1. Add the e4 tooling repository to the target. Don't check the “resolve dependent software” checkbox. (Current e4 tooling repo is listed here: https://wiki.eclipse.org/E4/Install)
  2. Add Plugin org.eclipse.e4.tools.css.spy to your application dependencies.
  3. Add command <fc #FF0000>org.eclipse.e4.css.OpenSpy</fc> to the keybindings to use it in the own app.
    E.g. M3+Shift+F5 (M3 is Alt)
Tooling i.imgur.com_tjxth.jpg
Plug-In Inspector - Can inspect active screen element, to show which plugin has contributed the current view..
Hotkey: ALT+SHIFT+F1
OnBoard in Eclipse 4
Menu-Inspector can inspect which menu was contributed by which plugin
Hotkey: ALT+SHIFT+F2 and menu click

Useful to find plugins, which contribute functionality (e.g. about dialog).
Onboard in Eclipse e4
Live-Editor - Can CHANGE active application's model at runtime
Hotkey: ALT+SHIFT+F9.

  1. Add Plugins:
    1. org.eclipse.e4.tools.emf.liveeditor,
    2. org.eclipse.e4.tools.emf.ui,
    3. org.eclipse.e4.tools.emf.ui.script.js +
    4. transitive dependencies to your application dependencies.
  2. Add command <fc #FF0000>e4.tooling.livemodel</fc> to the keybindings, to use it in the own app.

Add Plugin to the application to use it in the own app.

OnBoard in Eclipse 4
Context-Spy - Is able to inspect the Eclipse Context
Hotkey: ALT+SHIFT+F10.

  1. Add Plugins:
    1. org.eclipse.e4.tools.context.spy,
    2. org.eclipse.e4.tools.spy

Add Plugin to the application to use it in the own app.

http://download.vogella.com/luna/e4tools
or it may be downloaded under Marketplace

Glossar

useful Paths and Data

Path Code Example
Workspace Location
Platform.getInstanceLocation().getURL().getPath()
fare\workspace\
Bundle path
			import org.eclipse.core.runtime.FileLocator;
			import org.eclipse.core.runtime.Path;
			import org.eclipse.swt.SWT;
			import org.eclipse.swt.SWTError;
			import org.eclipse.swt.browser.Browser;
			import org.eclipse.swt.widgets.Composite;
			import org.osgi.framework.Bundle;
			import org.osgi.framework.BundleContext;
			import org.osgi.framework.FrameworkUtil;


		Bundle bundle = FrameworkUtil.getBundle(this.getClass());
		
		String pathIndexString = "/html/index.html";
		Path pathToIndex  = new Path(pathIndexString);
		
		//bundleentry://22.fwk9862796/html/index.html
		URL indexUrl = bundle.getEntry(pathIndexString);
		System.out.println("URL "+indexUrl);
		
		URL url = FileLocator.find(bundle, pathToIndex, Collections.EMPTY_MAP);
		URL fileUrl = null;
		try {
			//file:/D:/1PROJEKTE/EclipseRCP4Mp3Resizer/de.mine.application/html/index.html
			fileUrl = FileLocator.toFileURL(url);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		System.out.println("fileURL "+fileUrl);
file:/D:/1PROJEKTE/EclipseRCP4Mp3Resizer/de.mine.application/html/index.html
System Data
System.getProperty("user.dir")
java.version Java Runtime Environment version 
java.vendor Java Runtime Environment vendor 
java.vendor.url Java vendor URL 
java.home Java installation directory 
java.vm.specification.version Java Virtual Machine specification version 
java.vm.specification.vendor Java Virtual Machine specification vendor 
java.vm.specification.name Java Virtual Machine specification name 
java.vm.version Java Virtual Machine implementation version 
java.vm.vendor Java Virtual Machine implementation vendor 
java.vm.name Java Virtual Machine implementation name 
java.specification.version Java Runtime Environment specification version 
java.specification.vendor Java Runtime Environment specification vendor 
java.specification.name Java Runtime Environment specification name 
java.class.version Java class format version number 
java.class.path Java class path 
java.library.path List of paths to search when loading libraries 
java.io.tmpdir Default temp file path 
java.compiler Name of JIT compiler to use 
java.ext.dirs Path of extension directory or directories 
os.name Operating system name 
os.arch Operating system architecture 
os.version Operating system version 
file.separator File separator ("/" on UNIX) 
path.separator Path separator (":" on UNIX) 
line.separator Line separator ("\n" on UNIX) 
user.name User's account name 
user.home User's home directory 
user.dir User's current working directory

Eclipse RCP Product

The product allows to customize your application a little:

Launcher Icons and tycho

Here is how the icon for the launcher is set. When using tycho - the maven plugin, which is able to build rcp in headless mode - the paths to the images are set relative to the workspace. Otherwise tycho will not fid them.
You should use *.bmp files with alpha channel. The Tool Pixeformer is able to create such bmp files.
You should not use *.ico since there are bugs in tycho and not every *.ico file will work. Details here

Icons for windows

Since E4 default Window-Icons are set via the e4Model.

There is a possibility to set default Window-Icons for Windows without icons in code

    private void setDefaults() {
        Image icon16 = UtilsCommonResource.getCoreIcon(ImageSize.Size16x16);
        Image icon24 = UtilsCommonResource.getCoreIcon(ImageSize.Size24x24);
        Image icon32 = UtilsCommonResource.getCoreIcon(ImageSize.Size32x32);
        Image icon48 = UtilsCommonResource.getCoreIcon(ImageSize.Size48x48);
        Image icon256 = UtilsCommonResource.getCoreIcon(ImageSize.Size256x256);
        Window.setDefaultImages(new Image[] {icon16, icon24, icon32, icon48, icon256 });
    }
Customize the product to store the data inside the AppData folder

The product may store the data inside the user's AppData Folder. To do that some properties, which define the storage location for the setting must be set inside the product.

osgi.configuration.area - $APPDATA$/IVU/IVU.fare/.metadata/.plugins
osgi.instance.area - $APPDATA$/IVU/IVU.fare

As the result the above properties ae redirected into the user's directory. You can write the log files to that directory by including the ${osgi.instance.area}/log/application.log into the log path inside the properties.

and evaluating the path inside the log4j appender

/**
 * Unverändert der RollingFileAppender,
 * aber erlaubt die Angabe der Logdatei in File-URI-Notation.<br>
 * Dies wird notwendig, wenn die Logdatei über eine System-Property
 * definiert wird, die von Eclipse/OSGi bereitgestellt wird.<br>
 * <br>
 * Beispiel: log4j.appender.XXX.FileUri=${osgi.instance.area}/log/application.log<br>
 * 
 * @author xsb
 */
public class ExtendedRollingFileAppender extends RollingFileAppender {

    public void setFileUri(String fileUri) {
        
        URI uri = URI.create(fileUri);
        if (!"file".equalsIgnoreCase(uri.getScheme())) {
            throw new IllegalArgumentException("given uri is not a valid file uri: " + fileUri);
        }
        super.setFile(uri.getRawSchemeSpecificPart());
    }
}

here you can learn more about theses properties:
http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fruntime-options.html

Customization Runtimeparameters

There are multiple ways to configure an RCP Product. Here is a full list of parameters, which can be passed to Eclipse RCP Products: http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fruntime-options.html

<LAUNCHERNAME>.ini

Should only contain launch arguments.

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20120522-1813
-vm
C:\Programm Files\Java\JDK\1.5\bin\javaw.exe
-vmargs
-Xms40m
-Xmx512m
-Duser.timezone=Europe/Berlin
-Djna.library.path="C:/farelibs/32bit;C:/farelibs/64bit"
vmarguments

When adding vmarguments to the file

-Duser.language=es
-Dequinox.scr.waitTimeOnBlock=1
-Duser.timezone="Europe/Berlin"

/configuration/config.ini

Can hold any parameter, but should hold only VM arguments. For organisation' sake. The form is parameter=value

#This configuration file was written by: org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxFwConfigFileParser
#Mon Oct 14 17:21:58 CEST 2013
eclipse.p2.profile=DefaultProfile
osgi.framework=file\:plugins/org.eclipse.osgi_3.8.1.v20120830-144521.jar
equinox.use.ds=true
osgi.bundles=reference\:file\:org.eclipse.equinox.simpleconfigurator_1.0.301.v20120828-033635.jar@1\:start
org.eclipse.equinox.simpleconfigurator.configUrl=file\:org.eclipse.equinox.simpleconfigurator/bundles.info
eclipse.product=de.ivu.fare.rcp.core.bundle.product
osgi.splashPath=platform\:/base/plugins/de.ivu.fare.rcp.core.bundle
osgi.framework.extensions=reference\:file\:org.eclipse.osgi.nl_de_4.2.0.v20120721043402.jar
osgi.bundles.defaultStartLevel=4
eclipse.p2.data.area=@config.dir/../p2
eclipse.application=org.eclipse.e4.ui.workbench.swt.E4Application
fare.serverhost=localhost
fare.serverport=4447
osgi.nl=es_CO
fare.user=sov
fare.passwd=sovsovsov

Equinox (which is Elcipse implementation of OSGI API) options may be added to config.ini.

All Equinox runtime optins are listed here: here

Here are some of them:

// how many miliseconds should equinox wait until the service is ready. Default is 10.000. This pparameter may force equinox to launch much faster!
equinox.scr.waitTimeOnBlock=1

// logs execution time in ms
equinox.ds.perf=true

// prints the trace-logs on the console
equinox.ds.print=true

// debugmode on
equinox.ds.debug=true

// which start level should the bundles have on default? This should be larger than the launch-level of your product, otherwise the fragments will wait for core, which is not started yet.
// check the product > configuration > Start level
osgi.bundles.defaultStartLevel=4

Runtime Parameters

Can pass runtime arguments or vmarguments. The vmarguments must come as last parameter, because all aruments after are treated as Java VM Arguments.
The Java VM Arguments must be separated by a = sign.

The vmargument proper.ty=value is passed as as runtimeparameter as “-vmargs -Dproper.ty=value”

fare.exe  -my -runtimeargumentshere  -vmargs -Duser.language=es
fare.exe  -fare.mandant DEF -fare.passwd sovsovsov -fare.user sov  -vmargs -Duser.language=es
fare.exe  -osgi.nl es_CO

<fc #FF0000>ACHTUNG</fc>:

  1. Die Vm-Arguments fangen mit D an und kommen als letztes nach -vmargs nach allen anderen argumenten: -vmargs -Duser.language=es_CO
  2. Bei OSGI Argumenten wird das Präfix “osgi.” weggelassen.
    Ausserdem ist wird das Gleichheitszeichen weggelsassen. Syntax -parameter value.
    Bei Systemproperty osgi.nl : -nl es_CO

Run Configuration

Use run configuration during the development to set VM Args:

Application Model

The Applicationmodel looks as following :

It's parts are describedd here:

PartDescriptors

Can define Part-Templates. The templates will be used to create predefined MParts:

        @Inject
        MPartStack mainStack;
        @Inject
        IEclipseContext stackEclipseContext;
...
        IEclipseContext newContext = EclipseContextFactory.create();
        newContext.setParent(stackEclipseContext);

        MPart part = partService.createPart("partdescriptor.template.id");
        part.setContext(newContext);

        mainStack.getChildren().add(part);

API

The e4 Static classes, like services are listed here: http://wiki.eclipse.org/Eclipse4/RCP/EAS/List_of_All_Provided_Services

Basic Eclipse entrypoints
Command What
org.eclipse.core.runtime.Platform

is a central class, which represents the platform itself. Has static methods only. Use it to:
manage installed plugin-registry,
show/hide splash screen,
check if in develpment mode,
manage authorization

org.eclipse.ui.PlatformUI

The central class for accessing to the Eclipse workbench.

MApplicationDescribes the application object. Can be used for example to add new windows to your application
interface MWindow Represents a Window in your application. Can be used for example to add new windows to your application
This interface is used by MApplication to work with Windows inside of the EMF structure.
  • getMainMenu
  • getX
  • getY
  • getWidth
  • getHeight
  • getWindows
  • getSharedElements

ACHTUNG: to hide an MWindow you can do MWindow.setIsToBeRendered(false). The same is done, when a MWindow is closed, but then ALL CHildren (Parts etc.) are set to isToBeRendered=false. To enable MWindows content set MWindows as to be rendered:

        MWindow mWindow = findElements.get(0);
        mWindow.setToBeRendered(true);
        mWindow.setVisible(true);

        /*
         * ACHTUNG HACK: As soon as an MWindow is closed - it's children are set to
         * "isToBeRendered=false".
         * So Set the Children back to "isToBeRendered=true" again
         */
        for (MWindowElement child : mWindow.getChildren()) {
            child.setToBeRendered(true);
            child.setVisible(true);
        }

To connect the windows hierarchically - add the window to a parent window. Then the child window will always be in front its parent. They will come to front together

interface MTrimmedWindow Represents a Window in your application. The underlying SWT shell has been created with the SWT.SHELL_TRIM attribute which means, it has a tile, a minimize, maximize and resize button.
interface MPerspective Object for the perspective model element..
interface MPart extends MUIElements Are used by the MBasicFactory to create Parts.
interface MDirtyable Are used by the MBasicFactory to create Parts.
interface MPartDescriptor MPartDescriptor is a template for new Parts. You define in your application model a PartDescriptor. A new Part based on this PartDescriptor can be created via the EPartService and shown its showPart() method. Are used by the MBasicFactory to create Parts.
  • getMenus
  • getToolbar
  • isCloseable
  • getDescription
MBasicFactoryFactory, which can create Parts to insert them into the EMF Model. Available Methods:

			import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory;

			MBasicFactory.INSTANCE.createTrimmedWindow(); 
			MBasicFactory.INSTANCE.createTrimBar(); //the toolbar with icons at the top
			MBasicFactory.INSTANCE.createPart();
			MBasicFactory.INSTANCE.createWindow();
			MBasicFactory.INSTANCE.createPartSashContainer(); //a sash, which can contain parts
			MBasicFactory.INSTANCE.createPartStack(); //a stack, which can contain parts
			...
EModelService A very important class, which allows manipulating the EMF Model. \\

has a find() method to get Parts by

  • tags
  • id
  • Class

Can move() or clone() Parts.
Can find find the nearest parent context

eModelService.getContainingContext(mainStack);

EPartService
  • Can switch perspectives.
  • Can activate Parts
    partService.showPart(newMPart, PartState.ACTIVATE);
  • Can hide Parts
  • Can retrieve all dirty Parts
ECommandService Can be injected. Finds Commands.
CommandManager Can be injected. Finds Commands AND is able to add execution listeners to them.
EHandlerService

Can be injected. Finds Handlers.

    @Inject
    private IEclipseContext eclipseContext;

    @Inject
    private ECommandService commandService;

    @Inject
    private EHandlerService handlerService;

    
        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                for (Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {
                    Module module = (Module) iterator.next();

                    ParameterizedCommand command = commandService.createCommand("de.ivu.fare.commands.activateModule",
                            null);
                    handlerService.executeHandler(command, eclipseContext);

                }
            }
        });

    }

IEventLoopAdvisor

Displays a popup when an Exceptions occurs, which is not handled. Put that into the context to let this object handle unhandled exceptions. Default one shows exceptios in dialogs.

 context.set(IEventLoopAdvisor.class, new IEventLoopAdvisor() {...} 

MenuManagerRenderer

Merges MenuItems from MenuContributions with the given menu.

		// somehow find a menu in Application.e4xmi e.g. by using EModelService.class
		org.eclipse.e4.ui.model.application.ui.menu.MMenu mmenu;

		MenuManagerRenderer mmr = new MenuManagerRenderer();
		mmr.init(eclipseContext);
		ContextInjectionFactory.inject(mmr, eclipseContext);

		// this checks the e4 model for contributions with same id and merges them to the mmenu
		mmr.processContributions(mmenu, ID_CONTEXTMENU, false, false);

Command What
context.modify,
context.declareModifiable
context.modify(key, value) creates an unmodifyable pair. It can't be changed from another (sub)contexts. declareModifiable can declare a pair, which is changeable from other contexts. context.set/context.get can access the vars, created by modify too, changes are allowed, only if var is modifyable.
context.runAndTrack registers a runnable, which is called on every context change.
context.getActive(MyObjectClass) searches the given class in the whole active context chain. This is a possibility to search for objects in the hierarchy, below the own context.
IContextFunction Context can contain Functions, which will operate on the context values.

        //Creation
        slideContextObjects.get(item).set("MyFunction", new IContextFunction() {
            
            @Override
            public Object compute(IEclipseContext context) {
                if(context.get("Token") == "one"){
                    return 1;
                }
                return 2;
            }
        });
        
        //usage
        slideContextObjects.get("MyFunction"); //returns 1 or 2 depending on "Token" inside of the context

ContextInjectionFactory

Can inject inject Data from the Context into objects. Useful to instantiate @Injection annotated Clases manually. It copies all the Data from an IEclipseContext into an Object - it doesn't set the given Context

		
ManipulateModelhandler man = new ManipulateModelhandler();
		
//IEclipseContext content was injected into man
//the man should allready have an own iEclipseContext to copy the data in
ContextInjectionFactory.inject(man,iEclipseContext);

//instantiates the TestPart and injects it into the context
ContextInjectionFactory.make(TestPart.class, context);
		
man.execute();

EclipseContextFactory A factory for creating context instances

        MPart newMPart = MBasicFactory.INSTANCE.createPart();

        MPartStack mainStack = (MPartStack) eModelService.find(STACKID, mApplication);
        mainStack.getChildren().add(newMPart);
        
        //nearest context of the stack
        IEclipseContext eclipseContext = eModelService.getContainingContext(mainStack);

        // eclipseContext should be the Context of mainStack, which is the parent of newPart in model 
        IEclipseContext newContext = EclipseContextFactory.create();
        newContext.setParent(eclipseContext);
        newMPart.setContext(newContext);

EclipseContext.activate(), EclipseContext.getActive(), @Active An instance of an EclipseContext may be activated. Then the values, available in the active contexts will be accessable by getActive(). Additional you can monitor the values inside the active context instances by using the @Active Annotation.

        main(){
                IEclipseContext parentContext = EclipseContextFactory.create();
                IEclipseContext context = EclipseContextFactory.create();
                context.setParent(parentContext);
                
                context.set("OBJECTNAME", new MyObject("xy")); 
                context.activate(); //activate the context. Triggers the monitorMyObject() because the active MyObject value changed
                
                parentContext.getActive("OBJECTNAME"); //MyObject("xy")
        }       
        
        @Optional
        @Inject
        void monitorMyObject(@Active @Named(OBJECTNAME) MyObject newMyObject){
                ...
        }
        

Naming the Context for Debugging

// setting the EclipseContext name which will be displayed by debugger (toString Method)
eclipseContextForEditor.set(EclipseContext.DEBUG_STRING, "Context with editor data");

Useful Classes and Methods
Command What
org.eclipse.core.runtime.SafeRunner can run a runner safely, catching exceptions.
IAdapterFactory

There is a nice article about this here.
Summary: if you need the ability to dynamically cast an Object(Adaptable) into another - create a IAdapterFactory which will return an implementation of IAdapter. Implement the cast inside the IAdapter which is inside the IAdapterFactory.
(e.g. casting of Lists into an Nodes by creating a Node for every Array entry)
and register the IAdapterFactory to the Platform. After that you will be able to cast dynamically from Array to Nodes!!!! Endeed the returned Type will be IAdaptable, so you will have to do an explicite cast.

public class NodeListFactory implements IAdapterFactory {
  /** The actual conversion to a Node */
  public Object getAdapter(Object list, Class clazz) {
    if (clazz == Node.class && list instanceof List) {
      Element root = new Element("List");
      Iterator it = list.iterator();
      while(it.hasNext()) {
        Element item = new Element("Entry");
        item.appendChild(it.next().toString());
        root.appendChild(item);
      }
      return root;
    } else {
      return null;
    }
  }
}

/** Teach the platform to convertLists into Nodes. The Adaptor will return IAdaptable instances which will have to be casted manually.*/
Platform.getAdapterManager().registerAdapters( new NodeListFactory(), List.class);

/** Use the mechanism to cast List to Nodes*/
Node getNodeFrom(IAdaptable list) {
  Object adaptable = list.getAdapter(Node.class);
  if (adaptable != null) {
    Node node = (Node)adaptable;
    return node;
  }
  return null;
}

IEventBroker

Alterlative to the observer pattern.

ACHTUNG: The IEventBrokerTopics may not contain any cahracters:

. NOT ALLOWED
_ ALLOWED
Big characters ALLOWED

There is a possibility to use hierarchies in Topics:

  public static final String TOPIC_TODO_ALLTOPICS = 
      "TOPIC_TODO/*";
  
  public static final String TOPIC_TODO_NEW = 
      "TOPIC_TODO/NEW";
  
  public static final String TOPIC_TODO_DELETE = 
      "TOPIC_TODO/DELETED";
  
  public static final String TOPIC_TODO_UPDATE = 
      "TOPIC_TODO/UPDATED";
} 

More about the usage was written by Tom Shindl.
Eclipse 4 used the EventAdmin to communicate between Bundles. The howto can be found here.

Observer Problems The current event processing in Eclipse is centered on the listener mechanism (also commonly described as the “Observer” pattern). This is a good, relatively simple, and easily customizable approach. However, two drawbacks became apparent as its usage grew: forced lifecycle and multiple implementations.

I use “forced lifecycle” term to describe a situation in which lifecycle of the listener is tied to the lifecycle of the event provider. The listener can't subscribe to the event before event provider is instantiated. On the other end, the listener has to be decommissioned when the event provider is disposed. This seemingly small detail of tying the lifecycles translates into a lot of extra code and, depending on the application, might become a major bug source.

The “multiple implementations” problem is another aspect that grows with the software size. Even as parts of the pattern are provided by the Platform (ListenerList, SafeRunnable, Job), there is still code that needs to be added by every event generator to tie them together. From a memory consumption angle, every event provider has to instantiate and maintain its own listener list, even if, as often happens, nobody is listening or the particular event does not happen. A quick search shows over 200 places in the SDK code alone that create ListenerList's. Chances are, there are as many listener mechanisms that have their own implementations or use ListenerList indirectly.

IEventBroker vs. Observer The main difference to the observer is that an intermediary is introduced between the sender and the receiver: events are published to the Event Broker which dispatches them to the listeners subscribed to this event type.

In this approach listeners can subscribe and unsubscribe as they please, regardless if the particular event source exists. It means that there is one implementation that everybody can use without the need to write additional code. It also means that no extra processing will be done for events that do not happen. And we'll have no need for multitude of listener interfaces specific to each event.

So, is this the best thing since sliced bread? Well, it does have some downsides. The event broker becomes a rather sensitive point of the system. It has to perform well both in CPU timing and memory allocations. And the broker itself better be robust.

//register listener
IEventBroker eventBroker = (IEventBroker) eclipseContext.get(
    IEventBroker.class.getName());
eventBroker.subscribe(IUIEvents.ElementContainer.Topic, null,
    new EventHandler() {
        public void handleEvent(Event event) {
          if (event.getProperty(IUIEvents.EventTags.AttName)
              .equals(IUIEvents.ElementContainer.ActiveChild)) {
            Object newPart = event.getProperty(IUIEvents.EventTags.NewValue);
            if (newPart instanceof MPart) {
              // do something
            }
          }
        }
    }, /* headless */ false);
    

//sending Events
Map data = new HashMap();
map.put(IUIEvents.EventTags.AttName, IUIEvents.ElementContainer.ActiveChild);
map.put(IUIEvents.EventTags.Element, partStack);
map.put(IUIEvents.EventTags.OldValue, oldPart);
map.put(IUIEvents.EventTags.NewValue, newPart);
map.put(IUIEvents.EventTags.Type, IUIEvents.EventTypes.Set);
 
IEventBroker eventBroker = (IEventBroker) eclipseContext.get(
    IEventBroker.class.getName());
eventBroker.send(IUIEvents.ElementContainer.Topic, data);

//To receive EventBroker Notifications in a specific method - use the shortcut
private static final String EVENT_TOPIC_ID = "MY_EVENTTOPIC_ID";
@Inject
@Optional
public void handleEvent(@UIEventTopic(EVENT_TOPIC_ID) final EventBrokerModuleData eventBrokerModuleData,
		@Optional final EditorPage page, @Optional final IEclipseContext eclipseContext) {
	//LOGIC GOES HERE
}


Some Fallpits:

	//1. ACHTUNG: sending Platform Objects doesn't allways work! Better Send own objects! 
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, eventBroker);

	//here this method is not triggered
	@Inject
    @Optional
    public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EventBroker b) {
        System.out.println("receive now EventBroker");
    }

	
	//2. ACHTUNG: sending "this" did not work. Better create Objects to send with "new"
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, this);
	

	//3. ACHTUNG: subscribing for Interfaces does not work. Just classes.
	@Inject
	@Optional
	//public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) IEclipseContext c) {       	System.out.println("Subscribe for Interface IEclipseContext FAILS");	}
	public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EclipseContext c) {       	System.out.println("Subscribe for class EclipseContext WORKS");	}

	
	
	//4. receiving Object results in an unexpected behaviour! Sometimes it reacts on everything, sometims on nothing.
	@Inject
	@Optional
	public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) Object o) {
		System.out.println("receive now Object");
	}

	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, eventBroker);	
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, this);
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, "String");
	

	
	
	//5. sending and receiving String - works always. Test using Strings.
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, "String");
	
    @Inject
    @Optional
    public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) String s) {
        System.out.println("receive now String");
    }



	//6. sending and receiving Objects created by new - works
	eventBroker.send(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT, new EventRepaintTable());

    @Inject
    @Optional
    public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EventRepaintTable r) {
        System.out.println("receive now EventRepaintTable");
    }

The class which receives the EventBroker notifications is not coppled with the EventBroker. It may be destroyed by the Garbadge Collector if it is not stored somewhere.

// Fallpit garbadge collector
class SomeClass{

  SomeClass(){
  
    /* this anonymous class is created and stored nowhere.
     * It is intended to handle EVENTID_CREATE_DOMAINOBJECT events, but
     * It will be destroyed by the garbadge collector and events wont be received
     */ 
    new MyEventHandlerListener(){
    
         public void handleEvent(@EventTopic(LocalEventDispatcher.EVENTID_CREATE_DOMAINOBJECT) EventRepaintTable r) {
            System.out.println("receive now EventRepaintTable");
        }
    }
    
  }
}

UISynchronize

This Object is able to execute stuff on the UI thread.


@Inject
UISynchronize uisynchronize

..

            uiSynchronizer.asyncExec(new Runnable() {
                @Override
                public void run() {
                    //do something on UI Thread
                }
            });

IContributionFactory A factory , which can create an Object by ContributorURI. Can be used to create an Object to do something with it, before it will be connected to a Part.

@Inject
IEclipseContext iEclipseContext

@Inject
MApplication mApplication;
...

  Object partimplementation = IContributionFactory.create("bundleclass://com.example.pluginname/com.example.pluginname.gui.parts.TestAccordionPart", iEclipseContext);
  MPart part = (MPart) eModelService.find("my.part.id", mApplication);
  part.setObject(partimplementation);
  
  //its the same like doing 
  MPart part = (MPart) eModelService.find("my.part.id", mApplication);
  part.setContributionURI("bundleclass://com.example.pluginname/com.example.pluginname.gui.parts.TestAccordionPart");

FrameworkUtil This can retrieve a Bundle by class.

FrameworkUtil.getBundle(MyHandler.class);

        Class<?> clazz = this.getClass();

        // getting the bundle
        Bundle b1 = Platform.getBundle("de.ivu.fare.rcp.tariff.gui.swt");
        Bundle b = FrameworkUtil.getBundle(this.getClass());

        // getting the extension point by name. Now looking for the module contributed by the
        // current Bundle? How? Can't get the right Bundle!
        IExtensionPoint extPoint = Platform.getExtensionRegistry().getExtensionPoint("de.ivu.fare.rcp.guiprovider.swt");
        IExtensionPoint extPointMod = Platform.getExtensionRegistry().getExtensionPoint(
                "de.ivu.fare.rcp.moduleProvider");
                
        //getting the extensionpoint by bundle?

Injection Cycle

< 100% 50% >
Step Fallpits
1. run constructor code

The non static, @Injected annotated fields are not available at this step.
You cannot use of them inside the constructor. No parametrized constructors are allowed, where you need a special object as a parameter. (e.g. concrete parrent composite)
Workaround - is to create a special context, where you put in the concrete object and where you inject these objects into the constructor.

c2 = context.createChild();
c2.set( Composite.class, myParentComposite );
ContextInjectionFactory.make(TableToolbar.class, c2);

2. do field injection
3. do method injection
4. run @PostConstruct

Using Annotations to Inject Objects from Context

Add own Objects to the context, then inject it from there into own Objects

FALLPUTS:

  1. method, which is invokes should be PUBLIC
  2. when using own annotation - it should have @Retention(value=RetentionPolicy.RUNTIME)


//ACHTUNG: Retention is important, when using methods at runtime
@Retention(value=RetentionPolicy.RUNTIME)
@Inherited
@Target(value={java.lang.annotation.ElementType.METHOD})
public @interface MyPostGuiCreate {

}

public class InjectableObject {
	void talk(){
		System.out.println("xoxo im the InjectableObject");
	}
}

public class InjectContextHere {
        //ACHTUNG: method should be public to be reached by reflections
	@MyPostGuiCreate
	public void myPostGuiCreate(@Named("MyInjectableOject") InjectableObject injectableObject){
		injectableObject.talk();
	}
}

@PostConstruct
void postConstruct(){
	
	// create a context
	IEclipseContext context = EclipseContextFactory.create();
	
	// put some stuff into it
	InjectableObject injectableObject = new InjectableObject();
//		context.set(injectableObject.getClass().getName(), injectableObject); // works: @Inject InjectableObject injectableObject
//		context.set(InjectableObject.class, injectableObject); // works: @Inject InjectableObject injectableObject
//		context.set(InjectableObject.class.getName(), injectableObject); // works: @Inject InjectableObject injectableObject
//		context.set("MyInjectableOject", injectableObject); //works: @Inject @Named("MyInjectableOject") InjectableObject injectableObject OR myPostGuiCreate(@Named("MyInjectableOject") InjectableObject injectableObject){
	
	// create the object, which we will inject stuff into
	InjectContextHere injectContextInThisObject = new InjectContextHere();
	
	//now use the annotation
	ContextInjectionFactory.invoke(injectContextInThisObject, MyPostGuiCreate.class, context);
}

IPartListener and EPartService

The EPartService may be injected to listen for MPart lifecycle.
Following hooks are available and are triggered in the following order.

partVisible Is triggered when a Part: is created / closed, loses / gains focus
partHidden When part loses focus, <fc #FF0000>NOT when the part is closed</fc>
partDeactivated When part loses its focus, when it is closed
partBroughtToTop
partActivated Is triggered when a Part: is created,
Concrete cycles
new Part opening cycle
partVisible
partBroughtToTop
UILifeCycle.BRINGTOTOP <fc #FF0000>triggered after focus. Wrong MPart is injected!</fc>
partActivated
@Focus <fc #008080>(not available in IPartListener only as annotations in Part implementations)</fc>
UILifeCycle.ACTIVATE <fc #FF0000>triggered after focus. Wrong MPart is injected!</fc>
explicite selection (by click) of Part which was inactive
partVisible
partBroughtToTop
@Focus <fc #008080>(not available in IPartListener only as annotations in Part implementations)</fc>
UILifeCycle.BRINGTOTOP <fc #FF0000>triggered after focus. Wrong MPart is injected!</fc>
partActivated
UILifeCycle.ACTIVATE <fc #FF0000>triggered after focus. Wrong MPart is injected!</fc>
implicite unselection of a Part, through selection of another
partHidden</fc>
partDeactivated
Part closing cycle
partDeactivated

Additionally an artificaial event, when the part is detached may be implemented by using the following:


    // Part listener
    partService.addPartListener(new IPartListener() {
        	

            @Override
            public void partVisible(MPart part) {
                if(isDetached(part)){
                	// part is detached now!
                }
            }
        });

    }
    
    private boolean isDetached(MPart part){
		boolean isDeatached = false;
		MWindow mWindow = part.getContext().get(MWindow.class);
		
		/* Every Part has an MWindow somewhere in the e4 Model hierarchy.
		 * In DETACHED Parts the Parent of the MWindow is NULL.
		 * In NON detached Parts the Parent is not null and has the type "ApplicationImpl" which implements MApplication.class */
		if(mWindow.getParent()==null || !MApplication.class.isAssignableFrom(mWindow.getParent().getClass())){
			isDeatached = true;
		}
		return isDeatached;
    }

Annotations for Injections

Dependency Injections and annotations are describes on wiki.eclipse.org.

OSGI Annotations Meaning
@Inject Can be applied to constructor, method, field. The Platform will execute those, @Inject annotated constructors/methods and fill the fields.
  1. The @Inject annotated constructor will be called, when the object of current type is needed by the pltform.
  2. Then the @Inject annotated fields which are are filled with data from the Context.
  3. Then the @Inject annotated methods are exectued, which suppose to do initialization. (called initializer methods)


<fc #FF0000>IMPORTANT</fc>: The methods, which have an @Inject in their parameters - are called on every change of the injected parameter. The Selection can be implemented by using injections.

    @Inject
    private void getActivePart(@Named(IServiceConstants.ACTIVE_PART) MPart part) {
        activePart = part;
    }

@Optional
@Inject
@Optional is used together with @Inject. This Annotation tells the system, that this field is not required for the current class. However an @Option annotated Field will be filled by the system later, as soon, as the Object becomes available in the context. This feature can be used if an Object is not available on creation, but will be available in the later part of teh lifecycle.
class X{
@Optional
@Inject
EHandler eHandler; //the creation of class will not fail, if the EHandler is not yet available. However if the Object will be created in the context later it will magically appear here -  and myFunction will be able to use eHandler.
...
void myFunction(){
//use eHandler;
}
@Named Can be applied to classes and methods. Makes the system retrieve a concrete object from the context, chosen by a name-key.
If the object was put into the context, without a name-key, then the object's class is used as key automatically.
Eclipse puts some Objects into the context automatically, e.g.
  • @Inject @Named (IServiceConstants.ACTIVE_SHELL) Shell shell; - active shell
  • @Inject @Named (IServiceConstants.ACTIVE_PART) Shell shell; - active part
  • @Inject @Named (IServiceConstants.ACTIVE_SELECTION) Shell shell; - active selection
  • @Inject @Named (IServiceConstants.SELECTION) Shell shell; - (inactive?) selection
@Creatable Can be applied to classes. Makes system initialize the Object of this type automatically, if no object of this type is available in the context.
@Inject @Preference(nodePath=“my.plugin.id”, value=“dateFormat”) Preferences are retrieved by annotations, as described by vogella

<fc #FF0000>IMPORTANT:</fc> this annotation is defined in the Plugin requires a dependency to the plugin org.eclipse.e4.core.di.extensions
@Active

Accesses the active context. Can retrieve Active Parts, active Windows etc.

@Execute
public void execute(@Active MPart activePart) {
   ...
}

Further, when annotating a Value by @Active this value gets reinjected, every time when a content in the active context-chain changes!.


...
@Active
MyObject myObject;

void method(EclipseContext newContextWithMyObject){
 ...
 this.context = newContextWithMyObject; //myObject is reinjected at this point
}

void method(EclipseContext childContextWithMyObject){
 ...
 //even listening for child context values - works
 childContextWithMyObject.setParent(this.context); 
} 

<fc #FF0000>ACHTUNG: There only can by ONE method per lifeCycle annotation!</fc>

LifecyleAnnotation Meaning
1. @PostConstruct Is called after the class is constructed and the field and method injection has been performed. Create the GUI here.
SUPPORTS INHERITANCE. Is called on multiple inheritance levels if present!
2. @PostContextCreate

<fc #FF0000>Can be used in a special class which is registered inside of the Extension Point</fc>
org.eclipse.core.runtime.product inside of the property lifeCycleURI. See http://www.vogella.com/articles/Eclipse4RCP/article.html#lifecycle.

3. @ProcessAdditions

<fc #FF0000>Can be used in a special class which is registered inside of the Extension Point</fc>
org.eclipse.core.runtime.product inside of the property lifeCycleURI. See http://www.vogella.com/articles/Eclipse4RCP/article.html#lifecycle. Here the e4 Model is allready loaded. Use this hook to change the Model and model dependant Contexts

4. @ProcessRemovals

<fc #FF0000>Can be used in a special class which is registered inside of the Extension Point</fc>
org.eclipse.core.runtime.product inside of the property lifeCycleURI. See http://www.vogella.com/articles/Eclipse4RCP/article.html#lifecycle. This * method will be called after the @ProcessAdditions calls are done

5. UIEvents.UILifeCycle.APP_STARTUP_COMPLETE

Is an IEventBroker event. Triggers when the Application is up and running Introduced in e4.3.

@Inject
@Optional
public void appIsRunning(@UIEventTopic(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE ) 
  Event event) {
  // Do something
} 

5+. UIEvents.UILifeCycle.ACTIVATE

Is an IEventBroker event. Triggers when the E4Application-Part becomes visible.

IEventBroker broker = (IEventBroker) iEclipseContext.get(IEventBroker.class.getName());
broker.subscribe(UIEvents.UILifeCycle.ACTIVATE,new EventHandler() {
	@Override
	public void handleEvent(Event event) {
		System.out.println("LifeCycleManager: UILifeCycle.ACTIVATE");
	}
});

// alternative annotation: you can inject the UIEventTopic Event
@Inject
@Optional
public void partActivation(@UIEventTopic(UIEvents.UILifeCycle.ACTIVATE) Event event, MApplication application) {
  // retrieve data from event
  MPart activePart = (MPart) event.getProperty(UIEvents.EventTags.ELEMENT);
} 

5+. UIEvents.UILifeCycle.BRINGTOTOP

Is an IEventBroker event. Triggers when the E4Application-Part get Focus and window comes to the top.

IEventBroker broker = (IEventBroker) iEclipseContext.get(IEventBroker.class.getName());
broker.subscribe(UIEvents.UILifeCycle.BRINGTOTOP,new EventHandler() {
	@Override
	public void handleEvent(Event event) {
		System.out.println("LifeCycleManager: UILifeCycle.BRINGTOTOP");
	}
 });
});

5+. UIEvents.UILifeCycle.PERSPECTIVE_OPENED

Is an IEventBroker event.

broker.subscribe(UIEvents.UILifeCycle.PERSPECTIVE_OPENED,new EventHandler() {
	@Override
	public void handleEvent(Event event) {
		System.out.println("LifeCycleManager: UILifeCycle.PERSPECTIVE_OPENED");
	}
});

5+. UIEvents.UILifeCycle.PERSPECTIVE_SAVED

Is an IEventBroker event.

broker.subscribe(UIEvents.UILifeCycle.PERSPECTIVE_SAVED,new EventHandler() {
	@Override
	public void handleEvent(Event event) {
		System.out.println("LifeCycleManager: UILifeCycle.PERSPECTIVE_SAVED");
	}
});

5+. @Focus Indicates that this method should be called, once the Part gets the focus. It is required to set the focus on one user interface control otherwise certain workbench functionality does not work.
<fc #FF0000>DONT SUPPORT INHERITANCE! If present on multiple inheritance levels - only called inside the class on the top of the hierarchy!</fc>
5+ @PreSave Is called before the application model is saved. You can modify the model before it is persisted.
5+ @Persist Is called if a save request on the Part is triggered. Can be used to save the data of the Part.
6. @PersistState Is called before the model object is disposed, so that the Part can save its state.
7. @PreDestroy Is called before the class is destroyed. Can be used to clean up resources.

Handler Annotations

Sources: http://www.vogella.com/articles/EclipseRCP/article.html#commands_behaviorannotations

@Execute This method will be Executed when the handelr is started
@CanExecuteMehtod annotated by this must return true, in order for the Handler's menuitem to be enabled!

Eclipse Context Lifecycle

notes
  1. Command Handlers are created in a different context, than in which they are executed. So can't use injections in there.

Commands with Parameters

To send a Command with a parameter it should be defined in the Applicationmodel.

The matching handler will receive the command and the @Exectue annotated method will do logic behind it.
The command parameter can be injected using @Named annotation with the id of the command in it.
<fc #FF0000>ACHTUNG: the parameter have to be injected as METHOD PARAMETER, not as class variable.</fc>

    @Execute
    public void execute(@Optional @Named("de.ivu.fare.commands.preferences.parameter.moduleId") String moduleIdParamater) {
        ...
        
    @CanExecute
    public boolean canExecute(){
        return true;
    }

To execute the command programmatically (with parameters) do the following:

ECommandService commandService = eclipseContext.get(ECommandService.class);
EHandlerService handlerService = eclipseContext.get(EHandlerService.class);

/*
 * The map is filled with [String:parameterkey - String:value]
 * Can not pass any parameterkey here. Only those, which were
 * defined in model, as a Command Parameter.
 */
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("de.ivu.fare.commands.preferences.parameter.moduleId",
		module.getModuleId());

Command commandNoParms = commandService.getCommand("de.ivu.fare.commands.preferences");
ParameterizedCommand commandParametrized = ParameterizedCommand.generateCommand(
		commandNoParms, parameters);

handlerService.executeHandler(commandParametrized);

Declarative Services in e4 alias. Context Functions

By using declarative Services you can make an Iterface availabel, for injections in <fc #FF0000>functions(not as plain @Inject annotated fields.)</fc> A Factory is registered in xml, to return implementations of an Interface.

Fallpits
  1. this funcionality requires following bundles. Otherwise the serivecs wont be found.
    1. org.eclipse.core.runtime
    2. org.eclipse.equinox.ds
    3. org.eclipse.equinox.util
    4. org.eclipse.osgi.services
  2. And
    Bundle-ActivationPolicy: lazy

    must be set in the MANIFEST.MF file

  3. the service must be registered in the MANIFEST.MF file as
    Service-Component: OSGi-INF/service.xml

Infos:

< 100% 40% 60% >
Step Describtion
1. edit MANIFEST.MF register xml in MANIFEST.MF by adding the following lines
Service-Component: OSGI-INF/component.xml 
Bundle-ActivationPolicy: lazy
2. Dependencies Require Bundles
org.eclipse.equinox.util
org.eclipse.equinox.ds - Declarative service
3. Create Interface, Implementation, Factory Factory extends ContextFunction

public interface IContextHellower {
	void sayHello();
}

// you can add @Singleton to the implementing class. Then only one instance of the service will be created.
@Singleton
public class ContextHellower implements IContextHellower {
	@Override
	public void sayHello() {
		System.out.println("Hellou!");
	}
}

public class ContextHellowerCreationFunction extends ContextFunction {
	@Override
	public Object compute(IEclipseContext context) {
		return ContextInjectionFactory.make(ContextHellower.class, context);
	}
}
4. Create an XML: OSGI-INF/component.xml
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="de.vogella.e4.rcp">
   <implementation class="com.example.e4.rcp.services.ContextHellowerCreationFunction"/>
   <service>
      <provide interface="org.eclipse.e4.core.contexts.IContextFunction"/>
   </service>
   <property name="service.context.key" type="String"
      value="com.example.e4.rcp.services.IContextHellower"/>
</scr:component>
5. Inject
	@Inject
	public PlaygroundPart(IContextHellower contextHellower) {	
		System.out.println("Injected IContextHellower "+contextHellower);
	}

Adding a non model object to the model

If you need to use injections or other context features inside of a class, which isn't in the model - there are several ways to do so. Then this class will be injected into the Context AND YOU WILL BE ABLE TO USE INJECTIONS INSIDE THIS CLASS!

IEclipseContext.runtrack(Runnable) The runnable is called once on PostConstruct. IEclipseContext is passed to it. After that it is called every time when one of the context object which Runnable has used - changes.

    public TableCallbackHandler(IEclipseContext context) {

        context.runAndTrack(new RunAndTrack() {

            @Override
            public boolean changed(IEclipseContext context) {
                System.out.println("TableCallbackHandler#runAndTrack: Page activated");
                activeEditorPage = context.get(EditorPage.class);
                return true; // makes the method to be called again, when EditorPage.class changes
                             // in context
            }
        });
}

@Creatable Annotate the class by @Creatable. It can then be created by atomatically, on request from context. (e.g. on Injection or on
ContextInjectionFactory.make(TableCallbackHandler.class, context);

)

@Creatable
public class TableCallbackHandler {

    EditorPage activeEditorPage;
    @Inject
    public TableCallbackHandler() {

    @Inject
    public void onFocusChange(EditorPage page) {
        System.out.println("TableCallbackHandler#onFocusChange: Page activated");
        this.activeEditorPage = page;
    }

}
    
    ...
    TableCallbackHandler t = ContextInjectionFactory.make(TableCallbackHandler.class, context);

If the automaticall creation has to be done programatically - don't forget to put the newly created object into the context. It is not done by calling make only.

if (!eclipseContext.containsKey(TableCallbackHandler.class)) {
            Object handler = ContextInjectionFactory.make(TableCallbackHandler.class, eclipseContext);
            eclipseContext.set(TableCallbackHandler.class.getName(), handler);
}

Using Menu in Applicaiton.e4xmi

The menu in Applicaiton.e4xmi is represented by MMenu.
The menu may be rendered to SWT widgets as following:


	/**
	 * Same as the {@link #renderMenu(Menu, MMenuElement)} but skips the first menu object.
	 * Usable to flattern a menu object 
	 * 
	 * @param parentMenu - the parent menu
	 * @param menuElement - the element
	 */
	private void renderMenuSkipFirstLevel(Menu parentMenu, MMenu menuElement){
		for(MMenuElement childMenuElement : menuElement.getChildren()){
			renderMenu(parentMenu, childMenuElement);
		}
	}
	
	/**
	 * Method which iterates all {@link MenuContribution} children and renders them
	 * @param parentMenu - the menu which should be used as the parent for rendered {@link MenuItem} objects
	 * @param mMenuContribution - the element from the e4 model which contains menu items
	 */
	public void renderMenu(Menu parentMenu, MMenuContribution mMenuContribution){
		for(MMenuElement menuElement : mMenuContribution.getChildren()){
			renderMenu(parentMenu, menuElement);
		}
	}

	/**
	 * Renders the given {@link MenuElement} as a {@link Menu} and {@link MenuItem} structure.
	 * 
	 * @param parentMenu - the menu which should be used as the parent for rendered {@link MenuItem} objects 
	 * @param menuElement - the element from the e4 model which represents a menuItem
	 */
	@SuppressWarnings({ "unused", "restriction" })
	public void renderMenu(Menu parentMenu, MMenuElement menuElement){
		// not null
		Assert.isNotNull(parentMenu);
		
		if(menuElement instanceof MHandledItem){
			MHandledItem item = (MHandledItem) menuElement;
			MCommand mcommand = item.getCommand();
			String commandId = mcommand.getElementId();
			
			final ParameterizedCommand paramCommand =  commandService.createCommand(commandId, null);
			
			// swt
			MenuItem menuItem = new MenuItem(parentMenu, SWT.PUSH);
			menuItem.setText(item.getLabel());
			menuItem.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					handlerService.executeHandler(paramCommand, eclipseContext);
				}
			});
			
		}else if(menuElement instanceof MMenuSeparator){
			MMenuSeparator item = (MMenuSeparator) menuElement;
			
			// swt
			new MenuItem(parentMenu, SWT.SEPARATOR);
			
			
		}else if(menuElement instanceof MDirectMenuItem){
			MDirectMenuItem item = (MDirectMenuItem) menuElement;
			String contributionUri = item.getContributionURI();
			
			final Object handler = contributionFactory.create(contributionUri,eclipseContext);
			// swt
			MenuItem menuItem = new MenuItem(parentMenu, SWT.PUSH);
			menuItem.setText(item.getLabel());
			menuItem.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					ContextInjectionFactory.invoke(handler, Execute.class, eclipseContext);
				}
			});
			
			
		}else if(menuElement instanceof MDynamicMenuContribution){
			DynamicMenuContributionImpl item = (DynamicMenuContributionImpl) menuElement;
			String contributionUri = item.getContributionURI();
			
			final Object handler = contributionFactory.create(contributionUri,eclipseContext);
			
			// swt
			MenuItem menuItem = new MenuItem(parentMenu, SWT.PUSH);
			menuItem.setText(item.getLabel());
			menuItem.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					ContextInjectionFactory.invoke(handler, Execute.class, eclipseContext);
				}
			});
			
			
		}else if(menuElement instanceof MMenu){
			MMenu menu = (MMenu) menuElement;
			
			// swt			
			MenuItem mnItemSubmenu = new MenuItem(parentMenu, SWT.CASCADE);
			mnItemSubmenu.setText(menu.getLabel());
			String iconUri = menu.getIconURI();
			
			Menu swtSubMenu = new Menu(mnItemSubmenu);
			mnItemSubmenu.setMenu(swtSubMenu);
					
			for(MMenuElement subElement : ((MMenu) menuElement).getChildren()){
				renderMenu(swtSubMenu, subElement);
			}
		}
	}

A menu contribution allows adding menu points to the menu by menu-id.
The menu contribution may e.g. be created in another e4-model-plugin.

For that:

  1. Create a main-menu, here with id de.mine.e4.styleinspector.menu.context.basic
  2. Create a MenuContriibution and use Parent-Id to point to the main-menu de.mine.e4.styleinspector.menu.context.basic

Retrieving the Menu may be done as following. It is assumed that a Menu-contribution ID_CONTEXTMENU_CONTRIB and a menu ID_CONTEXTMENU exist in model.

	public static final String ID_CONTEXTMENU_CONTRIB = "de.mine.e4.styleinspector.menucontribution.cont1";
	public static final String ID_CONTEXTMENU = "de.mine.e4.styleinspector.menu.context.basic";


		// ACHTUNG: modelService.findElements does not find the **MenuContributions**. Have to iterate.
		// inspect the model and find the Menus
		MMenu mmenu = null;
		for(MMenuContribution mmc : app.getMenuContributions()){
			if(!mmc.getElementId().equals(ID_CONTEXTMENU_CONTRIB)){
				continue;
			}
			
			List<MMenu> listMMenu = modelService.findElements(mmc, ID_CONTEXTMENU, MMenu.class, null);
			if(!listMMenu.isEmpty()){
				mmenu = listMMenu.get(0);
			}
			break;
		}
		
		// assemble the menu, consider the Menu-Contributions with the same parent-Id
		MenuManagerRenderer mmr = getMenuManagerRenderer();
		mmr.processContributions(mmenu, ID_CONTEXTMENU, false, false);

UI Toolkits in Eclipse 4

Toolkit Example
  • TM alias Toolkit Model - Das Toolkit Model (TM) an EMF-Modell to generate UI-Layouts.
    You can write Toolkit models manually, or generate them.
    It generates Layout only, the concrete content (widgets) are bound to the layout at runtime.
    Runtime modelchanges are directly shown in layout.
    Because widgets are decoupled, they can run on a remote (web)server.
    • Based on EMF - is an Eclipse own toolkit project which takes UML like model (for modeling data) and generates java code automatically.
      eclipsesource.com, vogella.com
      • Based on XMI - is an XML based format, to save Object-metadata. It is a way to define 4th meta-level:
        1)Applicationmodel
        2)UML
        3)UML metamodel
        4)Meta-Metamodel - metamodel for UML
        • Based on XML.
  • XWT - Eclipse XML Window Toolkit. It takes declarative XML like models and generates UI layouts and widgets.
    During runtime the XWT reads the XML model and converts it to Java's SWT Wdgets.
    There is a special Editor for this.
    • Based on XML.
<Shell xmlns="http://www.eclipse.org/xwt/presentation"
    xmlns:x="http://www.eclipse.org/xwt">
    <Shell.layout>
       <FillLayout/>
    </Shell.layout>
    <Button text="Hello, world!">
    </Button>
</Shell>

CSS

Fallpits
What Where
There is a cure presentation about CSS EclipseCon 2012 CSS Presentation.
There is a list of CSS Tags SWT- CSS Mapping
Examples about using CSS Tags http://tomsondev.bestsolution.at
Style It! The Eclipse 4 Styling Tutorial https://max-server.myftp.org/trac/e4/export/36/trunk/kai/doc/eclipsecon-2011/e4-styling-tutorial-eclipsecon-2011.pdf
adding a CSS file to the application

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
   <extension>
         <property
           name="applicationCSS"
           value="platform:/plugin/testing/css/default.css">
         </property>
   </extension>
</plugin>

Enable a new look and feel (rounded corners)

The howto is described here

Possibilities of CSS in e4

Here is a short list of customizable properties. More properties are listed here.

Java-Property CSS Attribute
Class maps to Selector
Background maps to
background-color:  rgb(255,255,255) rgb(0,0,0) 10%; // 10% means, that the color must have been switched to the next color. So at 10%the color is allready black.
background-color:  rgb(255,255,255) rgb(0,0,0) rgb(0,0,0) rgb(255,255,255) 10% 90% 100%; // color changes becomes black at 10%, stays black till 90%, becomes white at 100%
background-image: some url 
border
border-color: #FF0000;border-width: 3; border-style: dotted;
cursor
cursor:crosshair;
font
 
          font: italic 12 bold "Terminal";
          font-style: italic;
          font-size: 12; 
          font-weight: bold;
          font-family: "Terminal";
color
color: #FF0000 
class widget
.MTrimBar {
    background-color: #b3db18; 
}
.MTrimmedWindow { 
	background-color: #b3db18; 
}
Patterns can be defined as bg, e.g. patterns from here.
<property
	name="applicationCSSResources"
	value="platform:/plugin/de.mine.application/img/">
</property>

}<sxh css>
.MTrimmedWindow { 
    background-image: url("./pattern.png");

}
It is possible to select the active(focused) tabs, by using the “active” class. To define a default look - use more general CSS rule before defining the rule for special tabs.
/* match ALL Tabs, match ALL part stacks  */
CTabItem, CTabFolder{
  background-color: rgb(240,240,240);
}

/* OVERRIDES TWO GENERAL RULES ABOVE */
/* match Active part stack */
CTabFolder.active{
  background-color: white;
}
/* match tabs in ACTIVE(focused) part stack */
CTabFolder.active CTabItem{
  background-color:white;
}
/* match SELECTED tab in ACTIVE(focused) part stack */
CTabFolder.active CTabItem:selected{
  background-color:rgb(200,212,0);
}
Classes and ids

//VOGELLA
// IStylingEngine was injected
// via @Inject

Label label = new Label(parent, SWT.NONE);
Text text = new Text(parent, SWT.BORDER);

// Set the ID, must be unique in the same window
IStylingEngine.setID(label, "MyCSSTagForLabel"); 

//alternatively set id 
label.setData("org.eclipse.e4.ui.css.id",
"MyCSSTagForLabel2asId");

//or set class
label.setData("org.eclipse.e4.ui.css.CssClassName",
"MyCSSTagForLabel2asClass");

// Set the class, can be used several times
IStylingEngine.setClassname(text, "error"); 




//ECLIPSE CON

//set class
WidgetElement.setCSSClass(widget, “class string”);

// set id
WidgetElement.setID(widget, “id”);

Eclipse Tags are mapped to classes in css.

dashboardPart.getTags().add("dashboardPart");
dashboardPart.getTags().add("dashboardCashier");

/* match all dashboard parts: color of content pane */
*[class='MPart dashboardPart'] {
	background-color: rgb(255,174,201);
}
/* match tabs of dashboard parts: color of tab */
CTabItem[class="dashboardPart"] {
	tab-color: rgb(255,174,201);
}
/* match tabs of dashboard parts in ACTIVE(focused) part stack: color of tab */
CTabFolder.active CTabItem[class="dashboardPart"] {
	tab-color: rgb(255,174,201);
}

/* match cashier dashboard parts: color of content pane */
*[class='MPart dashboardPart dashboardCashier'] {
	background-color: rgb(106,181,255);
}
/* match tabs of cashier dashboard parts: color of tab */
CTabItem[class="dashboardCashier"] {
	tab-color: rgb(106,181,255);
}
/* match tabs of cashier dashboard parts in ACTIVE(focused) part stack: color of tab */
CTabFolder.active CTabItem[class="dashboardCashier"] {
	tab-color: rgb(106,181,255);
}

PseudoElements

Shell:parented { background: red; }
Text:focus { background: parchment; }
CTabItem:selected{  background-color:rgb(200,212,0);}
CTabFolder:selected{  white;}

Psedoelements can be inspected with the css spy :

Extend CSS by own properties

Add the following to the plugin.xml, as stated here

<!-- define the new property -->
<extension point="org.eclipse.e4.ui.css.property.handler">
		<handler adapter="org.eclipse.e4.ui.css.swt.dom.CTabFolderElement"
			composite="false"
			handler="o.e.e4.ui.css.swt.properties.custom.CSSPropertyCornerRadiusSWTHandler">
		<property-name name="swt-corner-radius"/>
		<property-name name="corner-radius" deprecated="renamed as swt-corner-radius"/>
	</handler>
</extension>

//define a Handler
    public class XXXPropertyHandler implements ICSSPropertyHandler {
        public boolean applyCSSProperty(Object element, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        ...
        }
    
        public String retrieveCSSProperty(Object widget, String pseudo, CSSEngine engine) throws Exception {
                return ...;
        }
    }

Scripting

The Model can be edited live by using the Live Editor, see LiveEditor. Import the necessary plugins and use ALT+Shift+F9 in your application.

Objects Methods Describtion
mainObject All members of the element, chosen in Live Editor The most importan Object
log debug(Objects)
error(Object)
Logger Object
swt newColor(Color)
newText(Composite, int)
new Label(Composite, int)
Can change SWT attributes here
eclipseContext get(String name) here you can enter the current EclipseObject
service getStyleEngine()
getPartService()
getModelService()
Can get Services
di newInstance(String, String, String)
execute(Object)
Object can get all Objects, available per Dependency injection

Example:

mainObject.getWidget().setBackground(swt.newColor("#00ff00"));
var meinpart = service.getModelService().find("de.vogella.e4.rcp.part.playground", mainObject);
var meinuri = meinpart.getContributionURI();
log.debug(meinuri);

Model Processor

package de.mine.experiments.modelprocessor.processors;

import javax.inject.Inject;

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;

/** 
 * A processor, registerd in extension Point: org.eclipse.e4.workbench.model
 * Changes the existing model, as soon as the bundle , in which this processor is registered is loaded.
 *  
 * @author skip
 *
 */
public class Processor {
	
	@Inject 
	MApplication application;
	
	@Execute
	public void execute(){
		MWindow existingWindow = application.getChildren().get(0);
		MTrimmedWindow newWindow = MBasicFactory.INSTANCE.createTrimmedWindow();
		
		existingWindow.setX(300);
		existingWindow.setY(300);
		
		newWindow.setWidth(200);
		newWindow.setHeight(existingWindow.getHeight());
		
		newWindow.setY(existingWindow.getY());
		newWindow.setX(existingWindow.getX()+existingWindow.getWidth());
		
		application.getChildren().add(newWindow);
	}
}

<plugin>
   <extension
         id="id1"
         point="org.eclipse.e4.workbench.model">
      <processor
            beforefragment="true"
            class="de.mine.experiments.modelprocessor.processors.Processor">
      </processor>
   </extension>


</plugin>

Persistence

Model

The Model changes are persisted into the file

...runtime-EclipseApplication\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi
Arguments

Run configurations can have different, persistence relevant arguments:

Argument Meaning
-clearPersistedState

Clears persisted model data (window size, position, open parts..) on every application

-clearPersistedState
-persistState

If the app was started with this parameter - changes wont be persisted:

-persistState true

In code you can check, whether the -clearPersistedState was set, by injecting named data:

    @Inject
    @Named(E4Workbench.CLEAR_PERSISTED_STATE)
    private boolean clearPersistedState;

@PersistState

To trigger persistence you should use the annotation @PersistState, see annotations

TAGS

The Tags are defined and explained inside of the Interface org.eclipse.e4.ui.workbench.IPresentationEngine The following Tags can be added to the Parts:

NoTitle - hide part title
NoClose - prevents parts from being closed (no lcose button)
NoAutoCollapse - prevents MPartStack from autocollapse, when empty.(grey area) The Tag should be applied to Area, containing the MPartStack.
NoMove - prevents parts from being dragged out from stack
Minimized
Maximized
MinimizedByZoom
Horizontal
Vertical
e4_disabled_icon_image_key
e4_override_icon_image_key
e4_override_title_tool_tip_key
Animations Enabled

OSGI

ClassLoaders

Every Eclipse Bundle has an own class loader. The mechanism is described here: http://www.martinlippert.org/publications/JS-Classloading-in-Eclipse-final-web.pdf

Logging in OSGI

Achtung On default there already are OSGI Listeners, which redirect the Exceptions to STDout.
So e.g. OSGI Messaging (EventBroker) Exceptions should be visible in the Eclipse Console and Log.
But sometimes the Exception-messages just do not appear there, until Eclipse is restarted.

OSGI implements a Fascade, behind which any framework like log4j may be used. This is done, because logging is done per Bundle, which is not possible with other logging frameworks.

There are LogService and LogReaderService which may be retrieved inside the Activator. Listeners may be installed to the LogReaderService.

Here is the summary of what is done below:

  1. you retrieve the LogReaderService from the Platform
  2. you add an own LogService
  3. inside the LogService you can log int oyour own file (e.g. using SLF4j)

The OSGI Platform will do the following:

  1. Some event will occur, e.g. because of an Exception
  2. The Eception will be bundled into an LogEntry
  3. The class LogTracker will be triggered
  4. LogTracker will pass the events on to platform services org.osgi.service.log.LogService and org.eclipse.equinox.log.ExtendedLogService (extends LogReaderService)
  5. the platform service org.eclipse.equinox.log.ExtendedLogService will pass the event to your LogService


import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogListener;
import org.osgi.service.log.LogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OSGILogListenerImpl implements LogListener {

    private static final Logger LOG = LoggerFactory.getLogger(OSGILogListenerImpl.class);

    @Override
    public void logged(LogEntry entry) {
        if (entry.getMessage() != null) {
            switch (entry.getLevel()) {
            case LogService.LOG_ERROR:
                LOG.error("Bundle: {}, Message: {}", entry.getBundle(), entry.getMessage(), entry.getException());
                break;
            case LogService.LOG_WARNING:
                LOG.warn("Bundle: {}, Message: {}", entry.getBundle(), entry.getMessage(), entry.getException());
                break;
            case LogService.LOG_DEBUG:
                LOG.debug("Bundle: {}, Message: {}", entry.getBundle(), entry.getMessage(), entry.getException());
                break;
            case LogService.LOG_INFO:
                LOG.info("Bundle: {}, Message: {}", entry.getBundle(), entry.getMessage(), entry.getException());
                break;

            default:
                break;
            }
        }
    }

}


import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogReaderService;

/**
* This class should be registerd as the Bundle Activator for a bundle, which is loaded early (low **osgi start level**)
*/
public class OSGILogActivator implements BundleActivator {

    private OSGILogListenerImpl osgiLogListener = new OSGILogListenerImpl();

    @SuppressWarnings({"rawtypes", "unchecked" })
    private LogReaderService getReaderService(BundleContext context) {
        LogReaderService readerService = null;
        ServiceReference readerRef = context.getServiceReference(LogReaderService.class.getName());
        if (readerRef != null) {
            readerService = (LogReaderService) context.getService(readerRef);
        }
        return readerService;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        LogReaderService readerService = getReaderService(context);
        if (readerService != null) {
            readerService.addLogListener(osgiLogListener);
        }
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        LogReaderService readerService = getReaderService(context);
        if (readerService != null) {
            readerService.removeLogListener(osgiLogListener);
        }
    }

}

In Eclipse you should use ExtendedLogReaderService to register to platform events, like errors on initialization of bundles.

The listener-Bundle should be loaded as early as possible, in an own bundle which has a low osgi start level This setting can be set within the runtim configuration or the product configuration.

Fragments

A plugin may contribute any element to the Application.e4xml

  1. Create a Model Fragment (every one may contribute to one part of Model-Element)
  2. Featurename is the name of the element, how it is called inside ApplicationModel.e4xml: addons, children

Startup faster

Add following to the VM arguments

equinox.scr.waitTimeOnBlock=1

ToolControl in Window Trim

Icons in the Toolbar are difficult to center. They are not created as children of Toolbar but as children of Composites, which are children of the ToolBar.

To set a fixed size to the composite and to center wom widget, like progressbar within do:


@PostComposite
public void createWidget(Composite parent){
        // centering nested composite
        Composite centering = new Composite(parent, SWT.NONE);
        centering.setBounds(0, 0, 30, 20);
        centering.setLayout(new GridLayout());
        // centering.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
        centering.setLayout(new GridLayout(1, false));

        // progressbar
        progressBar = new ProgressBar(centering, SWT.INDETERMINATE);
        progressBar.setMaximum(progressBar.getMaximum());
        GridData gd_progressBar = new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 1);
        gd_progressBar.widthHint = 60;
        progressBar.setLayoutData(gd_progressBar);

        // listener
        ToolItemSelectedListener listener = new ToolItemSelectedListener();

        // cursor
        centering.setCursor(cursor);

        // do the logic
        centering.addMouseListener(listener);
        progressBar.addMouseListener(listener);

        // tooltip here
        String toolTip = MessagesUtils.getNlsMessage(Messages.class, KeyExportsInProgress);
        progressBar.setToolTipText(toolTip);

Platform Files

platform.xml

In your Eclipse configuration directory in the org.eclipse.update folder, there should be a file named platform.xml. This is the file that the Update Configurator uses to determine the rest of the bundles that are known to your system.

The file is organized into sites. You should see a site with the location of platform:/base/ which basically means “everything from the plugins/ folder in my Eclipse install directory”. If you have created application extension locations via the update manager UI or if you have a links/ folder then you will have a site entry for each one you have created.

If your bundle exists in an enabled site that is in the platform.xml file, then everything should be ok. Note that the sites can list either features or plug-ins so you may need to know which feature your plug-in belongs to, in order to figure out if it should be known. See: https://wiki.eclipse.org/Where_Is_My_Bundle#Update_-_platform.xml

Eclipse RCP Project with Maven dependencies

Maven dependencies can not be added to Eclipse RCP projects driectly. It is possible to create a special RCP projects, which would resolve Maven dependencies automatically and share them with other plugins, which depend on them.

Several things have to be done by using maven-felix-plugin

  <packaging>bundle</packaging>
  
  <properties>
     <rcp.bundle.name>ru.mine.dependencies.maven</rcp.bundle.name>
  </properties>
  
  <!-- Add maven dependencies here -->
  <dependencies>
 		 <dependency>
		    <groupId>org.apache.logging.log4j</groupId>
		    <artifactId>log4j-api</artifactId>
		    <version>2.3</version>
		  </dependency>
		  
		  <dependency>
		    <groupId>org.apache.logging.log4j</groupId>
		    <artifactId>log4j-core</artifactId>
		    <version>2.3</version>
		  </dependency>
 
		<dependency>
		    <groupId>org.graylog2.log4j2</groupId>
		    <artifactId>log4j2-gelf</artifactId>
		    <version>1.1.0</version>
		</dependency>
  </dependencies>
  
  <build>
    <plugins>
    
      <!-- Make maven-bundle-plugins share all maven depenencies by adding jars to classpath and exporting all packages -->
	  <plugin>
	    <groupId>org.apache.felix</groupId>
	    <artifactId>maven-bundle-plugin</artifactId>
	    <extensions>true</extensions>
	    
	    <configuration>
	    	<manifestLocation>META-INF</manifestLocation>
		    <instructions>
				<Bundle-SymbolicName>${rcp.bundle.name};singleton:=true</Bundle-SymbolicName>
	            
	            <Export-Package>*</Export-Package>
	            
	            <Embed-Dependency>*;type=jar</Embed-Dependency>
	            <Embed-Directory>jars</Embed-Directory>
	            <Include-Resource>log4j/log4j.properties,log4j/Log4j2.xml</Include-Resource>
	            
	            <Embed-Transitive>true</Embed-Transitive>
	            <Import-Package></Import-Package>
	            
	            <Private-Package></Private-Package>
	            
	            <_failok>true</_failok>
	            <_nouses>true</_nouses>
	            
	            <Eclipse-BuddyPolicy>global</Eclipse-BuddyPolicy>
	            <Bundle-ActivationPolicy>lazy</Bundle-ActivationPolicy>
	            	            
	            <Bundle-ClassPath>.,{maven-dependencies},log4j</Bundle-ClassPath>
	             
	          </instructions>
          
	    </configuration>
	  </plugin>
      
    </plugins>
  </build>

</project>

Versioning

Eclipse bundles and features have version numbers of the form major.minor.micro.qualifier

1.0.0.SNAPSHOT
15.1.0.FINAL

Source: http://help.eclipse.org/kepler/index.jsp?topic=%2Forg.eclipse.pde.doc.user%2Ftasks%2Fpde_version_qualifiers.htm

You can modify the qualifier by different plugins. E.g. tycho-packaging-plugin modifies it for tycho builds:

			<plugin>
				<groupId>org.eclipse.tycho</groupId>
				<artifactId>tycho-packaging-plugin</artifactId>
				<version>${tycho.version}</version>
				<dependencies>
					<dependency>
						<groupId>org.eclipse.tycho.extras</groupId>
						<artifactId>tycho-buildtimestamp-jgit</artifactId>
						<version>0.23.1</version>
					</dependency>
				</dependencies>
				<configuration>
					<strictBinIncludes>false</strictBinIncludes>
					<format>'revvodoo1-'yyyyMMdd-HHmm-</format>
				</configuration>
			</plugin>

makes the qualifier to be the following after deploy (not inside the target folder)

1.0.0.revvodoo1-20150906-2225

UnitTesting

Read here about Jubula, Q7: Unit Testing the GUI

Examples by code

Creating a new Part

Add the following POJO into the Class URI field inside of Application.e4xml to create a new part.

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.ui.di.Focus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;

public class PartSwt {

	// @Inject annotated constructor will be used to create the Part
	@Inject
	public PartSwt(){
		System.out.println("Constructor");
	}

	// will be called with, to fill the new part with content
	@Inject
	@PostConstruct
	void onCreate(Composite partCanvas){
		partCanvas.setLayout(new FillLayout());
		Button b = new Button(partCanvas, SWT.NONE);
		b.setText("SWT Button");
		partCanvas.pack();
	}

	// will be called every time, when the new part retrieves focus
	@Focus
	void onFocus(){
		System.out.println("On part Focus");
	}
}

Manipulate the GUI programmatically

package de.vogella.e4.rcp.wizard.handlers;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.commands.MHandler;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MenuItem;

public class ManipulateModelhandler {
	
	    /*
	     * Application representation.
	     * Represents the top level EMF Container, entrypoint for getting all the EMF-Model Data:
	     * - Addons
	     * - Binding Context
	     * - Binding Tables
	     * - Handlers
	     * ...
	     * 
	     * Can retrieve active parts and everything, what is related to the model.
	     */
	    @Inject
	    private MApplication mapplication;
	    
	    /*
	     * Service, responsible for 
	     * - finding the parts of the EMF Model by Tag, Id, Class
	     * - manipulating the EMF Model, bringing them to the top etc.
	     */
	    @Inject
	    private EModelService eModelService;
	
	    //service, responsible for manipulating the model
	    @Inject
	    private EPartService ePartService;
    
		
		@Execute
		private void execute() {
		
			
			//1. mapplication : FINDING APPLICATION DATA CONTRIBUTED IN Application.e4xml
			mapplication.getHandlers();
			mapplication.getRootContext();
			mapplication.getSelectedElement();
			mapplication.getToolBarContributions();
			mapplication.getMenuContributions();
			
			System.out.println("\n Total Handlers: "+mapplication.getHandlers().size());
			for(MHandler handler : mapplication.getHandlers()){
				handler.getCommand().getCommandName();
				System.out.println("Found Handler which handles: "+handler.getCommand().getCommandName());
			}
			
			
			
			//2. eModelService : FINDING MODEL PARTS - use with  MUIElement, MWindow
			
			// Find objects by ID
			List<MPart> findElements = eModelService.findElements(mapplication, "de.vogella.e4.rcp.wizard.part.0", MPart.class, null);
			System.out.println("InjectionHandler: Found part(s) : " + findElements.size());

			
		    // Find objects by type
		    List<MPart> parts = eModelService.findElements(mapplication, null, MPart.class, null);
		    System.out.println("Found parts(s) : " + parts.size());


		    // Find objects by tags
			List<String> tags = new ArrayList<String>();
		    tags.add("justatag");
		    List<MUIElement> elementsWithTags = eModelService.findElements(mapplication, null, null, tags);
			System.out.println("Found parts(s) : " + elementsWithTags.size());

		    // Get the MWindow and change its size
			List<MWindow> windows = eModelService.findElements(mapplication, null, MWindow.class, null);
			MWindow mWindow = windows.get(0);
			mWindow.setWidth(mWindow.getWidth()-10);
			
			

			/* ATTENTION: RootContext contains is the context, in which the commands can be activated, 
			 * like "dialogAndWindow". It is not the Bundle Context!  */
//			mapplication.getRootContext();

			IEclipseContext iEclipseContext1 = mapplication.getContext();
			IEclipseContext iEclipseContext2 = eModelService.getActivePerspective(mWindow).getContext();

			//get MTrimmedWindow from the context or by eModelService. Injecting MTrimmedWindow will fail
			MTrimmedWindow mTrimmedWindow1 = iEclipseContext1.get(MTrimmedWindow.class); //null
			MTrimmedWindow mTrimmedWindow2 = iEclipseContext2.get(MTrimmedWindow.class); //found
			MTrimmedWindow mTrimmedWindow3 = eModelService.findElements(mapplication, null, MTrimmedWindow.class, null).get(0); //found
			
			
			
			//3. eModelService : MANIPULATING MODEL PARTS - use with  MUIElement, MWindow
			
			//find "Demo" menu
			MMenu mMenuDemo = null;
			for( MMenuElement menu : mTrimmedWindow2.getMainMenu().getChildren()){
				System.out.println("\nTop Menue :"+menu.getLabel() + " Element Id: "+menu.getElementId() ); //File Help Extras
				
				if(menu.getElementId()!= null && menu.getElementId().equals("de.vogella.e4.rcp.prefs.menu.demo")){
					mMenuDemo = (MMenu) menu;
				}else{
					((MUIElement)menu).setToBeRendered(false);
				}
			}
			
			
			//EDIT: adding menu entries doesn't work yet. The do not appear.
			//add an Exit menu-Entry
			if(mMenuDemo != null){
				MDirectMenuItem newExitItem = MMenuFactory.INSTANCE.createDirectMenuItem();
				newExitItem.setLabel("New Exit");
                                //ACHTUNG: not setContributORURI!!!!
				newExitItem.setContributionURI("bundleclass://de.vogella.e4.rcp.wizard/de.vogella.e4.rcp.wizard.handlers.QuitHandler");
				
				mMenuDemo.getChildren().add(newExitItem);
				
				newExitItem.setVisible(true);
				newExitItem.setToBeRendered(true);
			}
			

			
			//moving parts around
			MUIElement partPlayground = eModelService.find("de.vogella.e4.rcp.wizard.part.playground", mapplication);
			MUIElement partDetails = eModelService.find("de.vogella.e4.rcp.wizard.part.details", mapplication);

			//move the part to a new parent
			if (partPlayground.getParent() != partDetails.getParent()){
//				eModelService.move(partPlayground, partDetails.getParent()); //ATTENTION: ArrayIndexOutOfBoundsException
				eModelService.move(partPlayground, partDetails.getParent(), 0);
			}
			
			
			
			//3. MBasicFactory : CREATING MODEL PARTS
			MBasicFactory.INSTANCE.createTrimmedWindow(); 
			MBasicFactory.INSTANCE.createTrimBar(); 		//the toolbar with icons at the top
			MBasicFactory.INSTANCE.createPart();
			MBasicFactory.INSTANCE.createWindow();
			MBasicFactory.INSTANCE.createPartStack(); 			//a container, which makes parts appear as tabs
			MBasicFactory.INSTANCE.createPartSashContainer(); 	//a container, which can arrange stacks horizontally or vertically 

			//create a new Part, attach it near playground
			MPart newMPart = MBasicFactory.INSTANCE.createPart();
			newMPart.setLabel("NewPart");
			newMPart.setParent(partPlayground.getParent());

			//create a new Window
			MWindow mWindow = MBasicFactory.INSTANCE.createWindow();
			mapplication.getChildren().add(mWindow); 

		}
		
		
		
		
		
		private void wait(int sec){
			int start = Calendar.getInstance().get(Calendar.SECOND);
			
			while(Calendar.getInstance().get(Calendar.SECOND) - start <sec){
				Display.getCurrent().readAndDispatch();
			}
		}
}

Create a new MWindow, with a custom Shell

The Shell for the MWindow is created by the WBWRenderer.class in context of the window. Here we inject a custom, extended WBWRenderer into the context.

MWindow mWindow = MBasicFactory.INSTANCE.createWindow();
app.getChildren().add(mWindow);
mWindow.getTags().add(WBWRendererCustomTags.WINDOWTAG_CLOSERESIZE);

mWindow.setRenderer(new WBWRendererCustomTags());
Shell s = (Shell) mWindow.getWidget();

stylingEngine.setClassname(s, CSS_CLASS_WINDOW);
stylingEngine.setId(s, CSS_CLASS_WINDOW);
if (cssClassName != null) {
    stylingEngine.setClassname(s, cssClassName);
}

for (Control c : s.getChildren()) {
    c.setVisible(false);
}
s.pack();
return s;

Loading Image from Bundle

		Label labelMessages = new Label(parent, SWT.NONE);
		
		Bundle bundle = FrameworkUtil.getBundle(ControlTrimbarMessages.class);
		URL fileURLImage = bundle.getEntry("/icons/showcat_co.gif");
		
		URL fileUrl = null;
		try {
			fileUrl = FileLocator.toFileURL(fileURLImage);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		Image image = ImageDescriptor.createFromURL(fileUrl).createImage();
		labelMessages.setImage(image);

FAQ

	- Welche klassen haben einen Kontext?
		-> die meissten model Klasen haben einen context
	- wie ist die Kontext hierarchie? Ist es die Gleiche, wie im EMF Model? Context vom Parent Window ist das Parent-kontext von unterwindows?
		-> in einem der Tutorials gibts eine uebersicht.
	- wie kann man ein MWindow entfernen? (dispose)
		-> man kann auf das zugrundeliegende Widget zugreifen. Dieses muss man casten und disposen.
	- muss man einen Kontext initialisieren?
		-> ja , neu erstellte Model-Objekte haben einen null-Context