Dimitri Missoh
enthusiastic technologist and problem solver

August 2nd, 2009

BioLogic is out!

The logo of BioLogic

I’m happy to announce that the first version of BioLogic is out, and is available for download on SourceForge.

BioLogic is an Eclipse based RCP application I have developed for behavioral observation. The idea is based on Observer, a DOS application used for the same purpose. The aim of this application is to ease the work of scientists who want to record events during a behavioral observation.

An example of an observable process can be different behaviors a bumble bee shows when regulating brood temperature.

The example above is a real life Use-Case where BioLogic has been successfully utilized. At the Department of Behavioral Physiology & Sociobiology (University of Wuerzburg), BioLogic has been tested for the first time during experiments performed with bumble bees.

BioLogic allows the user to record, log, visualize, export and backup events collected during an experimental run.

The following screenshots give you an idea of the whole application:

The screen you seen when your start the workbench for the first time.

The screen you seen when your start the workbench for the first time.
The application's Quick Help.

Application’s Quick Help
Sequence recorded with the workbench.

Sequence recorded with the workbench.
The graphical visualization of a recorded sequence.

The graphical visualization of a recorded sequence.

What are the next steps:

  • Include an online help
  • Internationalize the application
  • Configure and let the user access the update site
  • Enhance the graphical visualization and make it printable and exportable
  • Implement the possibility to configure and persist more than one event key configuration

July 19th, 2009

Behind the scenes - Using dynamic proxies

Most of the time, a developer has to integrate a new functionality into an existing system. The challenge is not only to develop the piece of code that solves the problem but also to integrate the result of your implementation without breaking the running system and reuse most of the modules that already exist. In this article I would like to give you an example of how dynamic proxies can be used to achieve an integrating task on the low level (i.e. on the code level). First let’s have a look on some theoretical aspects.

Dynamic Proxy is a well known design pattern often used to protect or control the access to an object instance. A proxy object implements the same interface (or set of interfaces) of the object you want to control the access of. Instead of using the object itself, the proxy is passed to the client code.

All method call invocations are delegated to the proxy instance. The proxy instance makes most of the time either a pre-processing, a post-processing, handle the method invocation itself or just delegate the execution to the real object. Attentive readers will notice that this sounds like the decorator pattern. But there is a slightly difference between the decorator and the proxy pattern. The proxy pattern do not encapsulates the target object, it only acts as a delegate. For more details please have a look at the excellent article on JavaWorld.

Since Java 1.3, implementing dynamic proxies has been largely simplified by the introduction of the Dynamic Proxy API. See the following API description for more details. As mentioned above, to use dynamic proxies, you have to implement the same interface as your target object. But what if the target object do not implement any interface? That is exactly where the cglib library comes to the rescue. It is a code generation library used to extend Java class and implements interfaces at runtime. A prominent framework that makes an extensive use of it is Spring.

I would like to illustrate the use of dynamic proxies and especially the use of the cglib library through a very basic example. Suppose your company spent a lot of money to acquire a printing framework you now want to embed in your brand new product. Your product has a client-server based architecture where data are stored on a database accessible through the server.

With the client, you can browse the content of the repository. Your customers now want to be able to print out the content of a container. The printing frameworks can only handle some kind of files types. Furthermore let say that this framework is continually being enhanced. Every new release support more files types. The only fact we know about the framework is that it uses file extensions to determine which file types can be proceeded. The code the printing frameworks uses (which in fact is not modifiable) look like in the snippet below:

package com.dmissoh.proxies;
 
import java.io.File;
import java.util.Collection;
import java.util.Iterator;
 
/**
 * This is the class responsible for printing in a third party library.
 * In the real world, this class cannot be edited.
 
 *
 * The logic use in this class is simple. The file name is used to decide if it should be printed or not.
 
 *
 * Only files having the extension <strong>pdf</strong> will be proceeded. If this condition is fulfilled,
 * the <code>File.getAbsolutePath()</code> method is called to retrieve the full path, and print the file out.
 
 *
 * P.S.: A file that is going to be printed should also be accessible on the disc.
 *
 * @author Dimitri Missoh
 */
public class ThirdPartyPrinter {
 
	public void printOutFiles(Collection files) {
		if (files != null) {
			for (Iterator iterator = files.iterator(); iterator.hasNext();) {
				File file = iterator.next();
				String fileName = file.getName();
				if (fileName.indexOf(".pdf") &gt; -1) {
					String path = file.getAbsolutePath();
					System.err.println("Print the file: " + path);
				}
			}
		}
	}
}

Since files are physically located on the back-end we don’t want to download them all before sending them to the printing framework. This do not make sense because we know that the printing framework can handle only a predefined set of file types. In addition we want to be careful with the available bandwidth.

In the current version we know that the printing framework can only handle PDF files.

The quick and dirty approach will be:

  • first retrieve the list of files name available in the container
  • filter out only those having the extension PDF
  • send another server request to download only the PDF files
  • finally send the downloaded files for printing.

Well, this may be an acceptable solution until we get the next release of the printing framework which now handles more file types. We will than have to change the client code again to filter out not the new set of files the printing framework can now handle.
The printing framework calls two methods:

  • File.getName() to check if the file can be printed and,
  • File.getAbsolutePath() just before it prints the file.

Since the printing framework uses the java.io.File, the idea is to use cglib to create a proxy for the File object. The job of this proxy will be to download the file every time the File.getAbsolutePath() is called (since this method is called only for those files).

P.S.: Why can’t we just use the Dynamic Proxy API Java provides out of the box? The response is trivial, the File object do not implements any interfaces.

So let see how the new implementation could look like:

The PrintClient simulates the client code that calls the printing framework. It instantiates the file proxies (LazyFileDownloaderProxy) and request the printing job.

package com.dmissoh.proxies;
 
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
 
import net.sf.cglib.proxy.Enhancer;
 
/**
 * This class represents the client code, the one that calls
 * the third party library's API to print a given collection of files.
 *
 * Instead of calling the third party's APi with a list of <code>File</code>,
 * a collection of <code>LazyFileDownloaderProxy</code> are passed as arguments.
 * This proxies intercept method calls and allow us to make some preprocessing.
 *
 * @author Dimitri Missoh
 *
 */
public class PrintClient {
 
	/**
	 * The main method that calls methods in the third party library.
	 * @param args
	 */
	public static void main(String[] args) {
		Collection files = new ArrayList();
 
		File fileOne 	= (File) newInstance(File.class, "/home/docs/paper.txt");
		File fileTwo 	= (File) newInstance(File.class, "/home/docs/publication.pdf");
		File fileThree 	= (File) newInstance(File.class, "/home/docs/logo.gif");
		File fileFour 	= (File) newInstance(File.class, "/home/docs/book.pdf");
 
		files.add(fileOne);
		files.add(fileTwo);
		files.add(fileThree);
		files.add(fileFour);
 
		// Print all pdf-Files by calling the API in the third party library
		ThirdPartyPrinter fileChecker = new ThirdPartyPrinter();
		fileChecker.printOutFiles(files);
	}
 
	/**
	 * Returns an proxy instance that encapsulate a <code>File</code> object.
	 * @param clazz the <code>Class</code> to create the proxy instance for.
	 * @param argument the arguments to passed to the constructor.
	 * @return a proxy instance of the class passed as argument.
	 */
	@SuppressWarnings("unchecked")
	public static Object newInstance(Class clazz, Object argument) {
		try {
			Enhancer e = new Enhancer();
			e.setSuperclass(clazz);
			e.setCallback(new LazyFileDownloaderProxy());
			Class[] argumentTypes = new Class[]{String.class};
			Object[] arguments = new Object[]{argument};
			return e.create(argumentTypes, arguments);
		} catch (Throwable e) {
			e.printStackTrace();
			throw new Error(e.getMessage());
		}
	}
}

The LazyFileDownloaderProxy is the heart of our example. Method invocations done on the File object (through it proxy) are intercepted. If the called method is getAbsolutePath, the file will be first downloaded and the invocation is than delegated to the File object itself.

package com.dmissoh.proxies;
 
import java.io.File;
import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
/**
 * This class is used as proxy for a <code>File</code> object.
 * The aim of this class is to intercept method calls done on the File object
 * through this proxy.
 *
 * This proxy class dowload the file just before the File.getAbsolutePath() is called,
 * and than delegate the processing to the corresponding method of the <code>File</code> object.
 *
 *
 * @author Dimitri Missoh
 *
 */
public class LazyFileDownloaderProxy implements MethodInterceptor {
 
	/*
	 * (non-Javadoc)
	 *
	 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
	 * java.lang.reflect.Method, java.lang.Object[],
	 * net.sf.cglib.proxy.MethodProxy)
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		Object returnValueFromSuper = null;
 
		String methodName = method.getName();
 
		File file = null;
 
		if ((obj instanceof File)) {
			file = (File) obj;
		}
 
		// If the getAbsolutePath() method is called, download the file.
		if ("getAbsolutePath".equals(methodName)) {
			if (file != null) {
				downloadFile(file);
			}
		}
 
		// Than return the value as if it has been called on the corresponding method of the File class.
		try {
			returnValueFromSuper = proxy.invokeSuper(obj, args);
		} catch (Throwable t) {
			System.out.println("throw " + t);
			System.out.println();
			throw t.fillInStackTrace();
		}
		return returnValueFromSuper;
	}
 
	/**
	 * The dummy method called to download the file. This represent the time or resource
	 * consuming operation.
	 *
	 * @param file the file to download.
	 */
	private void downloadFile(File file) {
		for (int i = 0; i &lt; 3; i++) {
			try {
				Thread.sleep(1000);
				System.out.println("Downloading file '" + file.getName()
						+ "' ..." + (i + 1) + " second(s)");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

P.S.: From the cglib website, I download the library file that contains all dependencies (cglib-nodep-2.2.jar).

The screenshot below shows the project structure:

The project structure

The project structure

The project structure

Source files to this article can be either downloaded from this Blog or are available through the SVN repository https://homeworks.googlecode.com/svn/trunk/.

Download Projects Files.

February 15th, 2009

Nuts and bolts of the UI development: GUI Test of SWT and Eclipse Applications

To be honest, I’m not the programmer I’ve dreamed to be, the one that has 100% of his code test covered. As a GUI developer I had an excuse in the past, namely that there are no acceptable GUI testing frameworks one can rely on (open source and suitable for eclipse RCP Application). I tried some of them but wasn’t very happy:

Until the day I’ve found the SWTBot library developed by Ketan Padegaonkar. This library still under development but can already be used to write professional functional test for SWT and eclipse based applications. The API is simple, intuitive but powerfull. This framework has such a success, that in the meantime there is a proposal to move it to the eclipse.org foundation.

This tutorial describes step by step how to configure your IDE to be able to test your first RCP application. I hope that this warm-up will encourage beginners to adopt, put into practice and understand the importance of GUI testing in their daily work.

So let’s start:

System requirement:

  • The Java JRE 5.0 is installed on my PC
  • and I’am using the Eclipse IDE for Plug-in developer (Ganymed - version 3.4 SR1)

Step 1 - Installing SWTBot

  • Start your eclipse IDE and choose [Help/Install New Software...]
  • Than click Add Site and enter the following URl for the updatesite: http://swtbot.sourceforge.net/download.html. Than [OK]. You can also download the plug-ins directly if you prefere. See the SWTBot webpage for more details.
  • Select “SWTBot Eclipse Feature” and “SWTBot SWT Feature” and install them (your probably need to restart the workbench).

Step 2 - Download the Apache commons collection library

Since SWTBot depends on this library, it should be dowloaded and added in the next steps to the classpath.

Step 3 - Create the RCP application under test

  • We use the Mail Sample delivered with the IDE as the project under test. To create this project simple choose [File/New/Project/Plug-in Project] and enter the name “com.dmissoh.rcp.mail” or any name of your choice.
  • Access the next wizard page by clicking on next. In the “Plug-in options” group check the options “Generate an activator….” and “This plug-in will make contribution to the UI“.  Then check the option yes that corresponds to the question “Would you like to create a rich client application?
  • Choose “RCP Mail Template” on the next wizard page, and terminate with [Finish].

Step 4 - Create the test plug-in

  • As described in step 3 create a new plug-in project using [File/New/Project/Plug-in Project]
  • Do not use any template for this project
  • Name it “com.dmissoh.rcp.mail.test
  • Use the context menu on this project and add a the new folder “lib” as shown in fig. 1
  • Use the context menu on this project and add a the new folder “config
  • In the ‘config’ folder create a new file named log4j.properties and fill it with the content of the snippet below:
    log4j.rootCategory=DEBUG, R, O
     
    # Stdout
    log4j.appender.O=org.apache.log4j.ConsoleAppender
     
    # File
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=log4j.log
     
    # Control the maximum log file size
    log4j.appender.R.MaxFileSize=100KB
     
    # Archive log files (one backup file here)
    log4j.appender.R.MaxBackupIndex=1
     
    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.O.layout=org.apache.log4j.PatternLayout
     
    log4j.appender.R.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M(%F:%L) - %m%n
    log4j.appender.O.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M(%F:%L) - %m%n
  • Copy the jar file “commons-collections-3.2.1.jar” you previously unzip in step 2 into this “lib” folder.
  • Go the the plugins folder of your eclipse IDE (e.g. under “C:\Programme\eclipse3.5\eclipse\plugins”), and copy the following jars into the “lib” folder: net.sf.swtbot.eclipse.finder_1.2.0.921.jar, net.sf.swtbot.eclipse.spy_1.2.0.921.jar, net.sf.swtbot.eclipse.ui_1.2.0.921.jar, net.sf.swtbot.finder_1.2.0.921.jar as shown in fig.1.
  • Double click on the file “META-INF/MANIFEST.MF” of the project “com.dmissoh.rcp.mail.test”, select the tab “runtime” and use the “Add…” button to add the five jar that we’ve copied into the lib folder of this project. At the end, it should look like on fig. 2.
  • We now need to add some dependencies to our test plug-in. Click on the MANIFEST.MF file located in the META-INF folder of the test plug-in org.dmissoh.rcp.mail.test and open the tab Dependencies. Use the Add button to add both org.apache.log4j (bundle-version=”1.2.13″) and org.junit (bundle-version=”3.8.2″) to the dependencies. Fig. 3 show how it should looks like.

P.S.: This is the quick and dirty way to add third party jars into your project. It is always better to add all the jar file your project depends on in a another plug-in, and add this library plug-in as a dependency to your main plug-in.

Figure 1 - Project structure in the eclipse IDE

Figure 2 - The classpath after jars have been added.

Figure 3 - Project Dependencies

Step 5 - Writing the test class

It is now the time to write the test case.

  • Use the context menu on the ’src’ folder of the ‘org.dmissoh.rcp.mail.test’ plug-in project to add the new package org.dmissoh.rcp.mail.test
  • In this package create a new class RcpMailTestCase and fill it with the snippet below. P.S.: This Test Case class should extends the SWTBotEclipseTestCase. As you can see, this test cases checks the some of the basic functionalities the mail RCP application contains like the tree viewer and the about dialog.

package org.dmissoh.rcp.mail.test;
 
import net.sf.swtbot.eclipse.finder.SWTBotEclipseTestCase;
 
/**
 * A test case to run some basic unit tests on the Mail RCP sample.
 * @author Dimitri Missoh.
 */
public class RcpMailTestCase extends SWTBotEclipseTestCase {
 
	/**
	 * Test the about dialog
	 * @throws Exception
	 */
	public void testAboutDialog() throws Exception {
		/*
		 * Check that we can open the about dialog,
		 * the plug-in details dialog and
		 * the configuration details dialog.
		 * */
		bot.menu("Help").menu("About RCP Product").click();
		bot.button("Plug-in Details").click();
		bot.button("OK").click();
		bot.button("Configuration Details").click();
		bot.button("Close").click();
		bot.button("OK").click();
	}
 
	/**
	 * Test the dialog and the corresponding menu's command
	 * @throws Exception
	 */
	public void testDialog() throws Exception {
		/*
		 * Try to open the dialog using the menu.
		 * This is a way to make sure that this menu option is available.
		 * Also make sure that the dialog can be closed
		 * */
		bot.menu("File").menu("Open Message").click();
		bot.button("OK").click();
	}
 
	/**
	 * Test the navigation tree
	 * @throws Exception
	 */
	public void testTree() throws Exception {
		/*
		 * Now we try to navigate the tree.
		 * Make sure that the first tree node has 3 children
		 * */
		bot.tree().select("me@this.com");
		assertEquals(1, bot.tree().getTreeItem("me@this.com").getNodes().size());
		bot.tree().expandNode("me@this.com");
		assertEquals(3, bot.tree().getTreeItem("me@this.com").getNodes().size());
		/*
		 * Make sure that the second tree node has only one child
		 * */
		bot.tree().select("other@aol.com");
		assertEquals(1, bot.tree().getTreeItem("other@aol.com").getNodes()
				.size());
		bot.tree().expandNode("other@aol.com");
		assertEquals(1, bot.tree().getTreeItem("other@aol.com").getNodes()
				.size());
	}
 
	/**
	 * Test view related functions
	 * @throws Exception
	 */
	public void testView() throws Exception {
		/*
		 * Check that we have only two view opened on start:
		 * - the message view
		 * - the navigation view
		 * Than open one additional message view and check that we have one view more.
		 * At the end we close both message view and make sure that we have only one view left
		 * i.e. the navigation view.
		 */
		assertEquals(2, bot.views().size());
		bot.menu("File").menu("Open Another Message View").click();
		assertEquals(3, bot.views().size());
		bot.view("Message").close();
		bot.view("Message").close();
		assertEquals(1, bot.views().size());
	}
}

Step 6 - Running the test

Running the SWT bot test is as simple as running a classic JUnit test. Expect that we have to set the path to the log4j.configuration file in the corresponding Debug Configuration Dialog.

  • Click right on the RcpMailTestCase class in the package explorer and select [Run As/SWTBot Test], which is a new entry that should appears if the SWTBot feature as been successfully installed as described in Step - 1.
  • The test run will failed because the the path to the Log4J configuration file is missing.
  • Select from the menu [Run/Debug Configurations...] to open the debug configuration dialog
  • Select the corresponding SWTBot test (I’v called it MailTest)
  • In the Test-Tab select JUnit 3
  • In the Main-Tab select org.dmissoh.rcp.mail.product under [Program to Run/Run a Product:]
  • In the Arguments-Tab add the following entry to VM Arguments as shown on fig. 4:
    -Dlog4j.configuration=${project_loc:org.dmissoh.rcp.mail.test}/config/log4j.properties

Figure 4 - Debug Configuration

If all test runs without failure you should see the pleasant JUnit green bar at the end.

For those who are interested in the use of JUnit in general for RCP and OSGi applications, the following blog entries can be useful:

In his article Running Unit Tests for RCP and OSGi Applications, Patrick describes how sets of tests across multiple plug-ins or fragments can be run without using test suites. In one of his previous articles (Testing Plug-ins with Fragments) he explains how fragments can be used to separate the test code from the business code.

Download Projects Files.

January 25th, 2009

Interested in any key events of an SWT Application?

There is actually a way to achieve that. In an appropriate location of your code add:

Display.getDefault().addFilter(SWT.KeyDown, listener);

where the listener to implement can look like:

1
2
3
4
5
Listener listener = new Listener() {
   public void handleEvent(Event event) {
      // do some amazing things with the event
   }
};

Don’t forget to unregister your listener on dispose.

November 2nd, 2008

Disable or enable actions sets on perspective change in an Eclipse RCP application

Here is the challenge: I would like to enable or disable some action-sets according to the perspective the user activates. This should be done through a customization plug-in, because I’m not allowed to directly modify the code of the RCP application. I can only contribute my modifications through a new plug-in. Let’s walk through the necessary implementation steps. First we need to register our plug-in as a perspective change listener.  The second step will be to find a way to programmatically enable or disable a given action-set according to the new perspective.

Since I cannot modify the code of the existent application, I will create a new plug-in and extend the early start-up extension point org.eclipse.ui.startup to be able to run some code when the workbench starts. This extension point requires a class that implements the org.eclipse.ui.IStartup interface. The following snippet shows how the plug-in descriptor file plugin.xml should look like:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
   <extension
         point="org.eclipse.ui.startup">
      <startup
            class="perspectiveactionsets.startup.EarlyStartup"></startup>
   </extension>
</plugin>

To be notified on a perspective change event, a org.eclipse.ui.IPerspectiveListener will be registered to the active window of the workbench IWorkbenchWindow in the form of an anonymous class. I use the org.eclipse.ui.PerspectiveAdapter which provides default implementations for the methods described by the IPerspectiveListener interface. There is a very important point we should not forget when trying to retrieve the active workbench window (IWorkbenchWindow) using the static call PlatformUI.getWorkbench().getActiveWorkbenchWindow(). Let’s seek the javadoc for the getActiveWorkbenchwindow() method of the WorkbenchWindow class:

/**
* Returns the currently active window for this workbench (if any). Returns
* <code>null</code> if there is no active workbench window. Returns
* <code>null</code> if called from a non-UI thread.
*
* @return the active workbench window, or <code>null</code> if there is
*         no active workbench window or if called from a non-UI thread
*/
public IWorkbenchWindow getActiveWorkbenchWindow();

That is, we have to retrieve the active workbench window within the UI thread. Here is approximately how the earlyStartup() looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* (non-Javadoc)
* @see org.eclipse.ui.IStartup#earlyStartup()
*/
public void earlyStartup() {
  /*
  * The registration of the listener should have been done in the UI thread
  * since  PlatformUI.getWorkbench().getActiveWorkbenchWindow() returns null
  * if it is called outside of the UI thread.
  * */
  Display.getDefault().asyncExec(new Runnable() {
    /* (non-Javadoc)
    * @see java.lang.Runnable#run()
    */
    public void run() {
      final IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (workbenchWindow != null) {
          workbenchWindow.addPerspectiveListener(new PerspectiveAdapter() {
            /* (non-Javadoc)
            * @see org.eclipse.ui.PerspectiveAdapter#perspectiveActivated(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor)
            */
            @Override
            public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspectiveDescriptor) {
              super.perspectiveActivated(page, perspectiveDescriptor);
              // TODO implement the task to execute when the perspective change
            }
          });
        }
    }
  });
}

Now let us face the second problem. I would like to disable or enable some action-sets on perspective change.
In the eclipse IDE, you can customize a given perspective using the right mouse click on the corresponding perspective icons (see the screenshot below).

Customize a perspective using the context menu on the correpsonding icon

Customize a perspective using the context menu on the correpsonding icon

This opens a dialog where you can select the command groups (e.g. action-sets) that should be disabled in the workbench as shown in the following screenshot.

Disable command groups with the Customize Perspective Dialog

Disable command groups with the Customize Perspective Dialog

If we can manually customize it, it should also be possible to achieve this task with some lines of code. Let us consult the best eclipse book ever, i.e. the eclipse code itself. A quick search reveals that the problem has been solved in the class org.eclipse.ui.internal.dialogs.CustomizePerspectiveDialog. Having a look at the okPressed() in this class gives us the solution: a method turnOnActionSets() can be used on the perspective object to enable or disable an action-set.

After some modifications, the earlyStartup() method has now the form:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* (non-Javadoc)
* @see org.eclipse.ui.IStartup#earlyStartup()
*/
public void earlyStartup() {
  /*
  * The registration of the listener should have been done in the UI thread
  * since  PlatformUI.getWorkbench().getActiveWorkbenchWindow() returns null
  * if it is called outside of the UI thread.
  * */
  Display.getDefault().asyncExec(new Runnable() {
    /* (non-Javadoc)
    * @see java.lang.Runnable#run()
    */
    public void run() {
      final IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (workbenchWindow != null) {
          workbenchWindow.addPerspectiveListener(new PerspectiveAdapter() {
            /* (non-Javadoc)
            * @see org.eclipse.ui.PerspectiveAdapter#perspectiveActivated(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor)
            */
            @Override
            public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspectiveDescriptor) {
              super.perspectiveActivated(page, perspectiveDescriptor);
                if (perspectiveDescriptor.getId().indexOf("org.eclipse.debug.ui.DebugPerspective") &gt; -1) {
                  if (workbenchWindow.getActivePage() instanceof WorkbenchPage) {
                    WorkbenchPage worbenchPage = (WorkbenchPage) workbenchWindow.getActivePage();
                    // Get the perspective
                    Perspective perspective = worbenchPage.findPerspective(perspectiveDescriptor);
                    ArrayList toRemove = new ArrayList();
                      if (perspective != null) {
                        for (IActionSetDescriptor actionSetDescriptor : perspective.getAlwaysOnActionSets()) {
                          if (actionSetDescriptor.getId().indexOf("org.eclipse.search.searchActionSet") &gt; -1) {
                            // Add the action set descriptor to the list of the action sets to remove
                            toRemove.add(actionSetDescriptor);
                          }
                        }
                      perspective.turnOffActionSets((IActionSetDescriptor[]) toRemove.toArray(new IActionSetDescriptor[toRemove.size()]));
                    }
                  }
                }
            }
          });
        }
    }
  });
}

P.S.: In the example above, all search related actions (denoted by the ID ‘org.eclipse.search.searchActionSet‘) will disappear if you set the debug perspective (denoted by the ID ‘org.eclipse.debug.ui.DebugPerspective‘) as the active one.

For more details check out the source code of the project PerspectiveActionSets from the google code repository http://homeworks.googlecode.com/svn/trunk/ or download the plug-in project under the link below.

Download Projects Files.