Wednesday, December 5, 2012

Using JFreeChart in your RCP

JFreeChart is a charting library available under LGPL. Designed for AWT/Swing it takes some (minor) effort to make it work with SWT. There is a tutorial available from Lars Vogel which shows how to use this with swing, so I will focus on how to integrate this into your own plug-ins.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.External libraries needed for this tutorial are not provided on my github site!

Step 1: Create the JFreeChart plug-in

We will put all required libraries into a separate plug-in. First we need to download JFreeChart. Unzip the archive and switch back to Eclipse.

Create a new Plug-in from Existing JAR Archives. On the Jar selection page add jfreechart-1.0.19.jar, jfreechart-1.0.19-swt.jar and jcommon-1.0.23.jar from jfreechart/lib folder. You can do this by using the Add External... button.

On the next page set the Project name to org.jfree.chart and uncheck Unzip the JAR archives into the project. Then finish the wizard.

The manifest file is automatically opened. Switch to the Dependencies tab and add a dependency to org.eclipse.swt.

Step 2: Create a sample chart

For the view I created a separate plug-in. Add org.jfree.chart as a dependency and define a new view. The source code is pretty straight forward and mostly taken from Lars' tutorial.
package com.codeandme.jfreechart;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.util.Rotation;

public class DemoView extends ViewPart {

 @Override
 public void createPartControl(Composite parent) {
  final PieDataset dataset = createDataset();
  final JFreeChart chart = createChart(dataset, "Operating Systems");

  new ChartComposite(parent, SWT.NONE, chart, true);
 }

 private PieDataset createDataset() {
  final DefaultPieDataset result = new DefaultPieDataset();
  result.setValue("Linux", 29);
  result.setValue("Mac", 20);
  result.setValue("Windows", 51);
  return result;
 }

 private org.jfree.chart.JFreeChart createChart(final PieDataset dataset, final String title) {
  final JFreeChart chart = ChartFactory.createPieChart3D(title, dataset, true, true, false);
  final PiePlot3D plot = (PiePlot3D) chart.getPlot();
  plot.setStartAngle(290);
  plot.setDirection(Rotation.CLOCKWISE);
  plot.setForegroundAlpha(0.5f);
  return chart;
 }

 @Override
 public void setFocus() {
 }
}

To use JFreeChart under SWT you create a chart the same way as for Swing and display it using a ChartComposite (see line 21).

Your view should look like this:


If you use JFreeChart in your own products remember that it is licensed under LGPL. Consider buying the developers guide to support this great library.

Tuesday, November 27, 2012

Minimal eclipse-like application

For some of my applications I need to create a minimal eclipse like application. Something that uses a workspace, has preferences, help, p2, logger and the look and feel of eclipse. Yet I do not want JDT or any other programming language support. The question is: how would a minimal setup for such an application look like?

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: create a plug-in and a feature

As products generally should be feature based we first create an empty plug-in project named com.codeandme.eclipse.minimal and a feature com.codeandme.eclipse.minimal.feature that contains this project. Simple as that.

Step 2: create the product

Create a new Product Configuration named Minimal.product in your product plug-in.

On the Overview tab create a new Product Definition using the button on the righthand side. Select a nice Product Name and a unique Product ID. To use the same application as eclipse select org.eclipse.ui.ide.workbench.

Afterwards define your product to be feature based instead of the default plug-in based selection.

Switch to the Contents tab and add
  • org.eclipse.e4.rcp
  • org.eclipse.platform
  • com.codeandme.eclipse.minimal.feature
Hit Add Required to resolve additionally needed features. When you add features manually they usually are referenced by name and version number. You should get rid of the version information by selecting such features and removing the version string that can be found under Properties.... To depend on specific versions for your build use target definitions.

Save your product and test it by selecting Launch an Eclipse Application on the Overview tab. From there you can also use the export wizard to package your application.

If you do get an error 'No application id has been found' then you quite likely forgot to add your plug-in to your feature.

Sunday, November 25, 2012

Using your gerrit server in eclipse

So you followed the tutorial on how to setup a gerrit server and want to use it with eclipse?  Keep on reading...

We are using eclipse 4.2 here, not sure if all the required egit features are already present in previous versions.

Preparations

Gerrit is configured through its web frontend. As mentioned the first user to login will gain admin rights, so login with your administrator user. On the Projects tab you may Create a New Project.


I'm not going into details on project options here. The default settings should be sufficient for us to commit and review code.

Step 1: Configure a user

Now restart your browser, clear your passwords or do whatever necessary to get the authentication dialog back when accessing gerrit (in Firefox you might start a private session). Login with a user account, in the tutorial here I will use user1. You need to use the password you provided in the htaccess file on your server.

On the righthand side there is a Settings link. Fill in your Contact Information: provide your real name and a valid email address. You will receive a confirmation mail to verify your account.

Next add your SSH Public Key. If you don't have one yet you may create one in eclipse preferences.


Make sure you hit Save Private Key... otherwise your created key would not be saved permanently.

Back to gerrit: as a last step create a HTTP Password.

Every user of your system should follow these steps before he actually may use gerrit.

Step 2: Add gerrit repository in eclipse

The Git Repositories view allows to Clone a Git repository. The wizard will first ask for the repository type, obviously we want Gerrit. On the next page click on Add... and add your gerrit repository location.


The Server address is the same as you just entered in your browser before. Use your User ID and the htaccess file Password. Validate your settings before you hit Finish.

You should see your playground repository now. Gerrit allows to access this repository in multiple ways. We will use http (authorized) access in our example, but anonymous http and ssh will work, too.


Select the master branch and continue the wizard accepting the default settings.

Step 3: Committing & reviewing

Now you can start to share projects the same way as you would do it for git. On your first commit you might change a difference: There is a change ID provided for you:


On your first push you will be asked for credentials. The user name is still user1, but the password is the one you created on the gerrit webpage in your user settings. It is NOT the password from the htaccess file. Make sure you don't mix up those two.

You can see your change in gerrit by now:


Step 4: Mylyn integation

The gerrit web frontend is nice, but fun starts when you use mylyn integration. Unfortunately gerrit 2.5 support seems to be broken in Juno SR1 (see bug 393807). You still can use mylyn if you update your installation using the weeky builds update site: http://download.eclipse.org/mylyn/snapshots/weekly

I updated Mylyn Task List, Mylyn Reviews Connector: Gerrit and Mylyn Versions Connector: Git. Possibly updating the gerrit connector might be sufficient.

Open the Task List view to see pending review tasks. If you don't have defined queries yet you should find an "Unmatched" folder containing an entry for your first import. Now you can directly edit your reviews within eclipse.




Setup a dedicated gerrit server

Update:
This tutorial is somewhat outdated. Maybe relevant setup parts are still valid, however nowadays you should find plenty of ready-to-use docker images out there providing a working gerrit setup. I leave the tutorial online, just remember that it was written in 2012.


When you followed my previous post on git you might already have expected this one to come. Today we will setup a gerrit code review server.

A typical setup would use the integrated jetty server, an H2 database and openID for authentication. As this might not always be the first choice of components we will use a different approach here:

As a servlet container we will use tomcat running behind an apache webserver. We will use apache to deal with authentication so that we don't need openID for that. Finally we will replace H2 with mysql. To make this work we need to tweak a bit as especially mysql does not seem to be the first choice for gerrit programmers. For all you guys out there that deal with apache/tomcat/reverse proxy setups: please feel free to help to improve the setup as I (as a novice) struggled to make this work. To all the others: be warned that it might have some security issues.

That said, we are ready to start. Like the git server setup we will implement this on gentoo, my linux distribution of choice.

To actually work with gerrit you need access to a working SMTP server. I guess you'll figure out how to do this on your own.

Step 1: Install components

When building apache we need some extra modules named proxy, proxy_ajp and proxy_http. To enable these modules we need to add them to the APACHE2_MODULES configuration. So edit /etc/make.conf and add the line:
APACHE2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgi cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias proxy proxy_ajp proxy_http"
I left the other modules as defined by the default settings. If you are going to experiment with these you do this at your own risk.

Now install all needed components:
emerge apache tomcat mysql git commons-dbcp commons-pool
After starting apache and tomcat you should be able to see the welcome pages when pointing your browser to http://localhost and http://locahost:8080.
/etc/init.d/apache2 start
/etc/init.d/tomcat-7 start
Step 2: Configure mySQL

If you installed mysql for the first time, you need to configure it before starting it.
emerge --config =dev-db/mysql-5.1.62-r1
/etc/init.d/mysql start
mysql -u root -p
On the mysql shell enter following commands
CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'gentoo';
CREATE DATABASE gerritdb;
ALTER DATABASE gerritdb charset=latin1;
GRANT ALL ON gerritdb.* TO 'gerrit'@'localhost';
FLUSH PRIVILEGES;
exit;
We created a database gerritdb and a user gerrit using the password gentoo.

Step 3: Configure tomcat

We need a tomcat user to access the manager console. So edit /etc/tomcat-7/tomcat-users.xml
<tomcat-users>
    <role rolename="manager-gui"/>
    <user username="root" password="gentoo" roles="manager-gui"/>
</tomcat-users>
Additionally we need a database connector for the mysql database. Open /etc/tomcat-7/context.xml and add:
<Context>
    <!-- DB connector for gerrit -->
    <Resource name="jdbc/ReviewDb" auth="Container"
          type="javax.sql.DataSource" 
          maxActive="100" maxIdle="30" maxWait="10000"
          username="gerrit" password="gentoo"
          driverClassName="com.mysql.jdbc.Driver"
          factory="org.apache.commons.dbcp.BasicDataSourceFactory"
          url="jdbc:mysql://localhost:3306/gerritdb"/>
</Context>
Now we need to tell tomcat where to find commons-dbcp.jar and commons-pool.jar.
ln -s /usr/share/commons-dbcp/lib/commons-dbcp.jar /usr/share/tomcat-7/lib/
ln -s /usr/share/commons-pool/lib/commons-pool.jar /usr/share/tomcat-7/lib/
Restart tomcat to use the new settings:
/etc/init.d/tomcat-7 restart
Step 4: Configure apache

Apache will act as reverse proxy, which allows us to hide tomcat behind the apache webserver. Apache acts as a proxy between the client and the tomcat application. As we normally do not want to relay all traffic to tomcat we will create our own named vhost

First enable proxy support by changing APACHE2_OPTS in /etc/conf.d/apache2
APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE -D PROXY"
Add a new file /etc/apache2/vhosts.d/01_gerrit_proxy.conf to host our virtual server:
<IfDefine DEFAULT_VHOST>
Listen 81
NameVirtualHost *:81

<VirtualHost *:81>
        ServerName localhost
        Include /etc/apache2/vhosts.d/default_vhost.include

        <IfModule mpm_peruser_module>
                ServerEnvironment apache apache
        </IfModule>

        ProxyRequests Off
        <proxy *="">
                Order deny,allow
                Allow from all
        </proxy>

        ProxyPass /gerrit/ http://localhost:8080/gerrit/
        ProxyPassReverse /gerrit/ http://localhost:8080/gerrit/

        <Location /gerrit/login/>
                AuthType Basic
                AuthName "Gerrit Code Review"
                AuthBasicProvider file
                AuthUserFile /var/www/localhost/passwords
                Require valid-user
        </Location>
</VirtualHost>
</IfDefine>
We defined to use a ht password file to access gerrit, so we need to create it first.
htpasswd -c /var/www/localhost/passwords administrator # create first entry
htpasswd/var/www/localhost/passwords user1 # create additional entry
Remember that every gerrit user needs its own entry here.

Finally restart apache
/etc/init.d/apache2 restart
Step 5: Setup gerrit

Next we need to download gerrit. At the time of writing gerrit-full-2.5.war was the latest release. Rename your downloaded file to gerrit.war. Open up the local tomcat manager application http://localhost:8080/manager and scroll down to WAR file to deploy. Select your downloaded gerrit.war and deploy it.

Gerrit cannot install when binlogs are enabled in mysql. So lets disable them during setup. Modify /etc/mysql/my.cnf and comment out log-bin:
#log-bin
afterwards restart mysql
/etc/init.d/mysql restart
Now we are ready to setup gerrit:
java -jar /var/lib/tomcat-7/webapps/gerrit.war init -d /opt/gerrit
The setup script will ask some questions, see my installation log below for answers:
*** Gerrit Code Review 2.5
*** 
Create '/opt/gerrit'           [Y/n]? 

*** Git Repositories
*** 
Location of Git repositories   [git]: /var/lib/git/repositories

*** SQL Database
*** 

Database server type           [H2/?]: mysql

Gerrit Code Review is not shipped with MySQL Connector/J 5.1.10
**  This library is required for your configuration. **
Download and install it now [Y/n]? 
Downloading http://repo2.maven.org/maven2/mysql/mysql-connector-java/5.1.10/mysql-connector-java-5.1.10.jar ... OK
Checksum mysql-connector-java-5.1.10.jar OK
Server hostname                [localhost]: 
Server port                    [(MYSQL default)]: 
Database name                  [reviewdb]: gerritdb
Database username              [root]: gerrit
gerrit's password              : gentoo
              confirm password : gentoo

*** User Authentication
*** 

Authentication method          [OPENID/?]: http
Get username from custom HTTP header [y/N]? 
SSO logout URL                 : 

*** Email Delivery
*** 

SMTP server hostname           [localhost]: <enter your smtp server>
SMTP server port               [(default)]: <...>
SMTP encryption                [NONE/?]: <...>
SMTP username                  : <...>

*** Container Process
*** 

Run as                         [root]: 
Java runtime                   [/opt/icedtea-bin-6.1.11.5/jre]: 
Copy gerrit.war to /opt/gerrit/bin/gerrit.war [Y/n]? 
Copying gerrit.war to /opt/gerrit/bin/gerrit.war

*** SSH Daemon
*** 

Listen on address              [*]: 
Listen on port                 [29418]: 

Gerrit Code Review is not shipped with Bouncy Castle Crypto v144
  If available, Gerrit can take advantage of features
  in the library, but will also function without it.
Download and install it now [Y/n]? 
Downloading http://www.bouncycastle.org/download/bcprov-jdk16-144.jar ... OK
log-bin
Checksum bcprov-jdk16-144.jar OK
Generating SSH host key ... rsa... dsa... done

*** HTTP Daemon
*** 

Behind reverse proxy           [y/N]? 
Use SSL (https://)             [y/N]? 
Listen on address              [*]: 
Listen on port                 [8080]: 8081

*** Plugins
*** 

Prompt to install core plugins [y/N]? 

Initialized /opt/gerrit
Executing /opt/gerrit/bin/gerrit.sh start
Starting Gerrit Code Review:  * WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead
log-bin
 * WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead
OK
Waiting for server to start ... OK
Opening browser ...
Please open a browser and go to http://vserver:8081/#/admin/projects/
Your settings are stored in /opt/gerrit/etc/gerrit.config. See the configuration docs for details. In case you want to change some of them simply run the setup again.

SMTP data is up to you to provide. You'll need it as gerrit depends on sending emails.

You may revert the binlogs settings of mysql now if you wish.

As the setup automatically starts gerrit (and ssh, jetty) we have to stop it:
/opt/gerrit/bin/gerrit.sh stop
Some final settings to make tomcat happy:
chown tomcat:tomcat -R /opt/gerrit 
chown tomcat:tomcat -R /var/lib/git/repositories 
cp /opt/gerrit/lib/bcprov-jdk16-144.jar /usr/share/tomcat-7/lib/
cp /opt/gerrit/lib/mysql-connector-java-5.1.10.jar /usr/share/tomcat-7/lib/
Step 6: Fine tuning of mysql

Gerrit is a little picky about text encodings in mysql. Typically all fields should be encoded latin1_bin, but for the web frontend to work some of them need to be changed to utf8_bin. This seems to be a long lasting struggle between gerrit and mysql. To fix those issues you need to convert some text fields to utf8. I tried to locate them all by playing around with the web interface. You might change them by running following sql statements in mysql:
USE gerritdb;
ALTER TABLE `accounts` CHANGE `full_name` `full_name` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL , CHANGE `preferred_email` `preferred_email` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL;
ALTER TABLE `account_external_ids` CHANGE `email_address` `email_address` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL; 

If you encounter application errors, please consult /var/logs/tomcat-7/localhost.<current_date>.log and look for lines like this one:
com.google.gwtorm.server.OrmException: fetch failure on account_external_ids
It tells you that table acount_external_ids needs some tweaking. Here the field email_address needs conversion to utf8-bin.

As sql is not a language that I 'speak' fluently I used phpmyadmin to change the database layout.

Please let me know of any additional changes so that I can update this post.

Step 7: Restart servers

Now all that's left to do is restart your servers and add them to your default runlevel.
/etc/init.d/mysql restart
/etc/init.d/apache2 restart
/etc/init.d/tomcat-7 restart

rc-update add mysql default
rc-update add apache2 default
rc-update add tomcat-7 default
Point your browser to http://localhost:81/gerrit/ to see your server in action.

Take care to first login with your designated administrator user as the very first user to login to gerrit will automatically gain admin rights.

Final thoughts

The setup as described is working, but I guess the apache/tomcat setup might need some tweaking. I'd be glad to get some comments from more experienced users on this. Eg. after entering login data the user is redirected to an error site. When entering the base address for a second time you end up in the gerrit application.

Setting gerrit.canonicalWebUrl in the gerrit config file is highly recommended as gerrit wants to provide some links to itself. With the reverse proxy such links will only work when a correct host is specified here (like http://hostname:81/gerrit).

Tuesday, October 9, 2012

Integrating a custom builder

A builder can be used to trigger custom actions during a project build. You can use it to update resource files, generate documentation or to twitter every piece of code you write...

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Creating the builder

This is the easy part. Create a new Plug-in project named com.codeandme.custombuilder and switch to the Extensions tab of the plugin.xml.

Add an extension for org.eclipse.core.resources.builders. Set the ID to com.codeandme.custombuilder.myBuilder, leave the builder settings empty and create a run entry below. There set the class to com.codeandme.custombuilder.MyBuilder. Implement the class with following code:
package com.codeandme.custombuilder.builders;

import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;

public class MyBuilder extends IncrementalProjectBuilder {

 public static final String BUILDER_ID = "com.codeandme.custombuilder.myBuilder";

 @Override
 protected IProject[] build(final int kind, final Map<String, String> args, final IProgressMonitor monitor)
   throws CoreException {

  System.out.println("Custom builder triggered");

  // get the project to build
  getProject();

  switch (kind) {

  case FULL_BUILD:
   break;

  case INCREMENTAL_BUILD:
   break;

  case AUTO_BUILD:
   break;
  }

  return null;
 }
}

Do not forget to add a plug-in dependency for org.eclipse.core.runtime.

Your builder is done. Sure you need to add functionality to it, but this is your part. So now what? We need to add the builder to projects. To selectively add a builder eclipse suggests to use the Configure entry in the popup menu of projects.

Step 2: Create context menu entries

First lets create the commands to add and remove our builder.

Add the command definitions to your plugin.xml

      <command
            defaultHandler="com.codeandme.custombuilder.commands.AddBuilder"
            id="com.codeandme.custombuilder.addBuilder"
            name="Add Custom Builder">
      </command>
      <command
            defaultHandler="com.codeandme.custombuilder.commands.RemoveBuilder"
            id="com.codeandme.custombuilder.removeBuilder"
            name="Remove Custom Builder">
      </command>

At the same time we can add some additional dependencies:
  • org.eclipse.core.commands
  • org.eclipse.jface
  • org.eclipse.ui
Now implement the commands:

package com.codeandme.custombuilder.commands;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;

import com.codeandme.custombuilder.builders.MyBuilder;

public class AddBuilder extends AbstractHandler implements IHandler {

 @Override
 public Object execute(final ExecutionEvent event) {
  final IProject project = getProject(event);

  if (project != null) {
   try {
    // verify already registered builders
    if (hasBuilder(project))
     // already enabled
     return null;

    // add builder to project properties
    IProjectDescription description = project.getDescription();
    final ICommand buildCommand = description.newCommand();
    buildCommand.setBuilderName(MyBuilder.BUILDER_ID);

    final List<ICommand> commands = new ArrayList<ICommand>();
    commands.addAll(Arrays.asList(description.getBuildSpec()));
    commands.add(buildCommand);

    description.setBuildSpec(commands.toArray(new ICommand[commands.size()]));
    project.setDescription(description, null);

   } catch (final CoreException e) {
    // TODO could not read/write project description
    e.printStackTrace();
   }
  }

  return null;
 }

 public static IProject getProject(final ExecutionEvent event) {
  final ISelection selection = HandlerUtil.getCurrentSelection(event);
  if (selection instanceof IStructuredSelection) {
   final Object element = ((IStructuredSelection) selection).getFirstElement();

   return (IProject) Platform.getAdapterManager().getAdapter(element, IProject.class);
  }

  return null;
 }

 public static final boolean hasBuilder(final IProject project) {
  try {
   for (final ICommand buildSpec : project.getDescription().getBuildSpec()) {
    if (MyBuilder.BUILDER_ID.equals(buildSpec.getBuilderName()))
     return true;
   }
  } catch (final CoreException e) {
  }

  return false;
 }
}

package com.codeandme.custombuilder.commands;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;

import com.codeandme.custombuilder.builders.MyBuilder;

public class RemoveBuilder extends AbstractHandler implements IHandler {

 @Override
 public Object execute(final ExecutionEvent event) throws ExecutionException {
  final IProject project = AddBuilder.getProject(event);

  if (project != null) {
   try {
    final IProjectDescription description = project.getDescription();
    final List<ICommand> commands = new ArrayList<ICommand>();
    commands.addAll(Arrays.asList(description.getBuildSpec()));

    for (final ICommand buildSpec : description.getBuildSpec()) {
     if (MyBuilder.BUILDER_ID.equals(buildSpec.getBuilderName())) {
      // remove builder
      commands.remove(buildSpec);
     }
    }

    description.setBuildSpec(commands.toArray(new ICommand[commands.size()]));
    project.setDescription(description, null);
   } catch (final CoreException e) {
    // TODO could not read/write project description
    e.printStackTrace();
   }
  }

  return null;
 }
}

When retrieving the selected project we need to use the AdapterManager as some project types do not directly implement IProject (that is, if I remember correctly). Then we parse the build specification to add or remove our custom builder.

To add those commands to the Configure context menu we create a new menu contribution for popup:org.eclipse.ui.projectConfigure?after=additions

   <extension
         point="org.eclipse.ui.menus">
      <menuContribution
            allPopups="false"
            locationURI="popup:org.eclipse.ui.projectConfigure?after=additions">
         <command
               commandId="com.codeandme.custombuilder.addBuilder"
               style="push">
         </command>
         <command
               commandId="com.codeandme.custombuilder.removeBuilder"
               style="push">
         </command>
      </menuContribution>
   </extension>
Now you should be able to add and remove your builder.

Step 3: Selectively activate context menu entries

Only one of the commands makes sense regarding the current builder settings of a project. To enrich the user experience we will hide the invalid one.

Therefore we need to use a PropertyTester and some visibleWhen expressions as we did before in Property testers and Expression examples.

Create a new propertyTesters extension.

   <extension
         point="org.eclipse.core.expressions.propertyTesters">
      <propertyTester
            class="com.codeandme.custombuilder.propertytester.TestBuilderEnabled"
            id="com.codeandme.custombuilder.myBuilderTester"
            namespace="com.codeandme.custombuilder"
            properties="isEnabled"
            type="java.lang.Object">
      </propertyTester>
   </extension>

We leave the type to java.lang.Object as not all project types use a common base class (except Object of course). Implementing the property tester is straight forward:

package com.codeandme.custombuilder.propertytester;

import org.eclipse.core.expressions.PropertyTester;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Platform;

import com.codeandme.custombuilder.commands.AddBuilder;

public class TestBuilderEnabled extends PropertyTester {

 private static final String IS_ENABLED = "isEnabled";

 @Override
 public boolean test(final Object receiver, final String property, final Object[] args, final Object expectedValue) {

  if (IS_ENABLED.equals(property)) {
   final IProject project = (IProject) Platform.getAdapterManager().getAdapter(receiver, IProject.class);

   if (project != null)
    return AddBuilder.hasBuilder(project);
  }

  return false;
 }
}

Now add some visibleWhen expressions to your menu entries. View the final version of the plugin.xml online.

Monday, August 6, 2012

Setting up a dedicated git server


This tutorial explains how to set up a git server on linux. My distribution of choice is Gentoo, but the setup will be similar for other distributions.

Git allows to connect using several protocols:
  • git://
    Generally used to allow for anonymous access to a repository. As it does not support user authentication it is generally used for read access only.
  • ssh://
    The default protocol to use for git access. SSH logon will be done with the same username for all users. Git users will be diversified by the key they are using for authentication. So no password authentication shall be used here.
  • http://
    Access over a web frontend. Again for anonymous read access.
There are even more protocols available, but in this tutorial we will focus on these connection types.

As some of you might not want to install this on their own, I provide a ready to use VM image at the end of this post.

Step 1: Setup Gitolite

Gitolite is currently the git server of choice for the gentoo community. Set it up with
emerge gitolite

[ebuild  N     ] dev-vcs/gitolite-2.3.1  USE="-contrib -vim-syntax"
Portage will automatically create a user 'git' and set its homedir to /var/lib/gitolite, which is where we will host our git repositories.

Step 2: Configure Gitolite

As we need to login with our git user during setup provide some password for the user:
passwd git
For administration of repositories, git users and access rights we need an administration user. This can be any user on any system. Further on I will call this user GitAdmin.

So login with GitAdmin and create a public/private key pair if you don't have one yet.
ssh-keygen
Copy the public key to the git account
scp ~/.ssh/id_rsa.pub git@localhost:admin.pub
Login with your git account and initialize the git repositories:
su git
cd ~
gl-setup admin.pub
The setup will fire up an editor with the default settings. Change the repositories mask to
$REPO_UMASK = 0027;
This will allow group access to the repositories. It is important to set this during gl-setup as otherwise some generated folders would have too strict access conditions.

Now we can delete admin.pub and leave the git account.
rm ~/admin.pub
exit
Step 3: Managing users & repositories

Git keeps its own repository for administration. Switch back to your GitAdmin account and clone the gitolite-admin repository:
git clone git@localhost:gitolite-admin.git
Now you can change your master configuration file ~/gitolite-admin/conf/gitolite.conf
repo    gitolite-admin
        RW+     =   admin

repo    testing
        RW+     =   @all
You can add/remove repositories and users by changing this file. Gitolite tracks changes on that file and triggers all the required actions from that. See the documentation for details on the syntax.

For each username used in the gitolite.conf file there should exist an adequate public key file under ~/gitolite-admin/keydir. Eg for a user 'testuser' there should exist a file testuser.pub. Again you can find more info in the documentation.

After changing the configuration file we need to commit and push those changes.
cd ~/gitolite-admin
git add keydir conf
git commit -m 'adding some new users/repos'
git push origin master
The push triggers creation of new repositories or changing access rights.

ssh setup is complete and you can connect to a repository by using ssh://git@localhost/repositoryName.git.

The private key used for authentication will be used by git to grant correct repository access rights.

Optional: Setup git:// access

For git:// access we need to run the git daemon. First change some settings in /etc/conf.d/git-daemon
GITDAEMON_OPTS="--syslog --export-all --base-path=/var/lib/gitolite/repositories"

GIT_USER="gitdaemon"
GIT_GROUP="git"

Add the gitdaemon user.
useradd -g git -G git -s /sbin/nologin -d /dev/null gitdaemon
Start the service now and at boot time:
rc-update add git-daemon default 
/etc/init.d/git-daemon start

Hint: Please read the git-daemon documentation (man git-daemon) as you might not want to export all repositories found under --base-path. Eg. you normally would not export gitolite-admin.git this way.

Hint: If you run on a trusted network you might add --enable=receive-pack to GITDAEMON_OPTS which would allow for anonymous write access over git://.

Optional: Setup CGit web interface (http:// access)

CGit offers access to repositories over http. You need a webserver to run this cgi script. For this tutorial we will use a default apache installation.

At the time of writing cgit is masked by the ~x86 keyword, so you need to unmask it before emerging.
emerge cgit apache

[ebuild  N     ] www-servers/apache-2.2.22-r1  USE="ssl -debug -doc -ldap (-selinux) -static -suexec -threads" APACHE2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgi cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias -asis -auth_digest -authn_dbd -cern_meta -charset_lite -dbd -dumpio -ident -imagemap -log_forensic -proxy -proxy_ajp -proxy_balancer -proxy_connect -proxy_ftp -proxy_http -proxy_scgi -reqtimeout -substitute -version" APACHE2_MPMS="-event -itk -peruser -prefork -worker" 0 kB
[ebuild  N     ] app-admin/webapp-config-1.50.16-r4  102 kB
[ebuild  N     ] virtual/httpd-cgi-0  0 kB
[ebuild  N    ~] www-apps/cgit-0.9.0.2-r1  USE="vhosts -doc -highlight" 2,704 kB
Install it using webapp-config.
webapp-config -I -h localhost -d git cgit 0.9.0.2-r1
Edit the cgit configuration file /etc/cgitrc
# Enable caching of up to 1000 output entries
cache-size=1000 

# Specify the css url 
css=/git/cgit.css

# Use a custom logo
logo=/git/cgit.png

## List of repositories.
include=/etc/cgit-repos
As mentioned by webapp-config we will create our own file to manage available repositories: Execute following command on demand or run it using cron.
/usr/share/webapps/cgit/0.9.0.2-r1/hostroot/cgi-bin/cgit.cgi --scan-tree=/var/lib/gitolite/repositories >/etc/cgit-repos
Now add the apache user to the git group, start apache and add it to your default runlevel.
usermod -a -G git apache
/etc/init.d/apache2 start
rc-update add apache2 default
Point your favorite browser to http://<your-server>/cgi-bin/cgit.cgi and watch cgit in action.

Hint: You might want to hide the cgi-bin path within your urls. Therefore you can change /etc/apache2/vhosts.d/default_vhost.include and append some aliases at the end of the alias_module section
<IfModule alias_module>
        # Redirect: Allows you to tell clients about documents that used to
        # ... snip ...

        ScriptAlias /cgi-bin/ "/var/www/localhost/cgi-bin/"

        Alias /git/cgit.css "/var/www/localhost/htdocs/git/cgit.css"
        Alias /git/cgit.png "/var/www/localhost/htdocs/git/cgit.png"
        ScriptAlias /git/ "/var/www/localhost/cgi-bin/cgit.cgi/"
</IfModule>
After restarting apache you can use http://<your-server>/git/ as a starting point.

Further reading:

  1. Install gitolite on Debian and Gentoo 
  2. How gitolite uses ssh
  3. Configuring repositories
  4. Configuring users
  5. What your users need to know
VM image:

For your convenience you can download an OVA image, created with VirtualBox. I guess it will run with other virtualizers too.

Root password is "gentoo", I used the root user as GitAdmin.

In case your network is missing refer to the gentoo wiki on vmware and delete your udev persistent net rules file.

If you want to use portage you need to emerge --sync first as I deleted the tree for a smaller image size.

Thursday, August 2, 2012

Mirroring a p2 repository

For builds or for resolving a target platform eclipse often needs to connect to remote update sites. If you are behind a proxy or on a slow connection this can be annoying. A solution to that could be a local mirror of an update site.

Eclipse provides two applications to do exactly that (executed on a shell):
<eclipse install dir>/eclipse -nosplash -verbose -consoleLog -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source <update site> -destination<local folder>
<eclipse install dir>/eclipse -nosplash -verbose -consoleLog -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source <update site> -destination<local folder>
The first one mirrors artifacts like, plugins, features, additional libraries and so on. The second one mirrors metadata like bundle dependencies or repository content overview.

You can add proxy settings by providing http.proxyHost / http.proxyPort properties to the command. To mirror the Juno repository to C:\p2\Juno you would use:
<eclipse install dir>/eclipse -Dhttp.proxyHost=proxy.nowhere.com -Dhttp.proxyPort=80 -nosplash -verbose -consoleLog -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source http://download.eclipse.org/releases/juno -destination file:/C:/p2/Juno/
<eclipse install dir>/eclipse -Dhttp.proxyHost=proxy.nowhere.com -Dhttp.proxyPort=80 -nosplash -verbose -consoleLog -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source http://download.eclipse.org/releases/juno -destination file:/C:/p2/Juno/

For details see the online documentation or the wiki entry.

Tuesday, July 24, 2012

Reusing Platform images

The eclipse platform comes with a huge amount of ready to use images. Often when you need a nice icon for a toolbar or menu there is already one available within some other plug-in. Therefore plug-in programmers often copy over these icons to their own plug-in to reuse them. When you can be sure that the base plug-in will be available on your target installation you can use platform scheme URIs (see related post by Marcelo Paternostro) to reference such resources.

Still the hard part is to locate those images in other plug-ins. Therefore I wrote a plug-in, that allows you to search for such images.

Update:

This plugin is now part of Eclipse PDE and is maintained by the PDE team.

A screenshot is better than a 1000 words, so see for yourself:


Just select a source to browse, locate your desired image and click on it to update the Image Information.

Install it from the reopsitory: http://codeandme.googlecode.com/svn/trunk/at.pontesegger.updatesite/p2/

The view can be activated with Window -> Show View -> Other... and is located under Plug-in Development.

Be aware that some images you may find might be released under a restrictive license that forbids reuse.

Update 2012-07-29

* Fixed issue with memory errors when parsing too much images
* partially fixed support for e4 -> still you cannot browse the target platform there
* raised Bug 386197 to add this tool to PDE

Monday, July 2, 2012

A convenient toggle handler

Toggle buttons always seemed a bit awkward to me as there seems to be no uniform interface to store the toggle state. In the past I used Ralf Eberts ToggleHandler. But reading/setting and persisting the toggle state programmatically was always painful.

Now I rediscovered a post by Prakash G.R. and built my own ToggleHandler (view online) on top of this.

Using the handler

As stated in the referenced post the command contribution needs a state definition to store the current toggle state to:
<command      ...>       
   <state       
         class="org.eclipse.ui.handlers.RegistryToggleState:true"       
         id="org.eclipse.ui.commands.toggleState">       
   </state>       
</command> 

Then your handler just needs to extend AbstractToggleHandler. The toggle state will automatically be preserved when you close your application.

You can programmatically set the state of any such toggle command by executing
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
Command command = commandService.getCommand(commandId);
AbstractToggleHandler.setCommandState(command, true);

This will also toggle a visible button using this command.

Monday, April 16, 2012

Expression Examples

The previous post about the Expression Framework might be a bit confusing. Therefore I will provide some expression examples for eclipse built in source providers and tester and for the custom property tester provided in the previous post.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online. 

Preparations

Create a new Plug-in Project called com.codeandme.coreexpressions.samples. Add dependencies to org.eclipse.ui, org.eclipse.core.runtime and com.codeandme.coreexpressions. Set the content of plugin.xml to
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.commands">
      <command
            defaultHandler="com.codeandme.coreexpressions.samples.Login"
            id="com.codeandme.coreexpressions.commands.login"
            name="login">
      </command>
      <command
            defaultHandler="com.codeandme.coreexpressions.samples.Logout"
            id="com.codeandme.coreexpressions.commands.logout"
            name="logout">
      </command>
   </extension>
</plugin>
Add a new class com.codeandme.coreexpressions.samples.Login
package com.codeandme.coreexpressions.samples;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.ui.ISourceProvider;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.services.ISourceProviderService;

import com.codeandme.coreexpressions.ExampleSourceProvider;

public class Login extends AbstractHandler implements IHandler {

 @Override
 public Object execute(ExecutionEvent event) throws ExecutionException {

  ISourceProviderService service = (ISourceProviderService) PlatformUI.getWorkbench().getService(
    ISourceProviderService.class);
  ISourceProvider provider = service.getSourceProvider(ExampleSourceProvider.CURRENT_USER);
  if (provider instanceof ExampleSourceProvider)
   ((ExampleSourceProvider) provider).setCurrentUser("John Doe");

  return null;
 }
}
and another class com.codeandme.coreexpressions.samples.Logout
package com.codeandme.coreexpressions.samples;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.ui.ISourceProvider;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.services.ISourceProviderService;

import com.codeandme.coreexpressions.ExampleSourceProvider;

public class Logout extends AbstractHandler implements IHandler {

 @Override
 public Object execute(ExecutionEvent event) throws ExecutionException {
  ISourceProviderService service = (ISourceProviderService) PlatformUI.getWorkbench().getService(
    ISourceProviderService.class);
  ISourceProvider provider = service.getSourceProvider(ExampleSourceProvider.CURRENT_USER);
  if (provider instanceof ExampleSourceProvider)
   ((ExampleSourceProvider) provider).setCurrentUser(null);

  return null;
 }
}
We will use these commands in a later step of our tutorial.

The first commands we use will simply bind to the commandId=org.eclipse.ui.file.exit.

Test 1: Enable for a specific view

Our first expression will reveal a toolbar item in the Project Explorer view when the view is active.
      <menuContribution
            allPopups="false"
            locationURI="toolbar:org.eclipse.ui.navigator.ProjectExplorer">
            <command
                  commandId="org.eclipse.ui.file.exit"
                  label="Test1"
                  style="push">
               <visibleWhen
                     checkEnabled="false">
                  <with
                        variable="activePartId">
                     <equals
                           value="org.eclipse.ui.navigator.ProjectExplorer">
                     </equals>
                  </with>
               </visibleWhen>
            </command>
      </menuContribution>
We need to set checkEnabled on the visibleWhen element to false, otherwise the expression will not be active. The with section uses activePartId as source. It is a string containing the ID of the active view/editor (see description).

The equals section compares the source value with org.eclipse.ui.navigator.ProjectExplorer, returning true when the Project Explorer View is active.

Run your Plug-in as a new Eclipse application and activate the Project Explorer to see the toolbar entry appear/disappear.

Test 2: Enable when Projects are selected


Next we want a button to be visible when at least one project is selected.
            <command
                  commandId="org.eclipse.ui.file.exit"
                  label="Test2"
                  style="push">
               <visibleWhen
                     checkEnabled="false">
                  <with
                        variable="selection">
                     <iterate
                           operator="or">
                        <adapt
                              type="org.eclipse.core.resources.IProject">
                        </adapt>
                     </iterate>
                  </with>
               </visibleWhen>
            </command>
Now we use the selection source provider, which provides the current selection. The iterate operator is nice as it tests all elements of a collection. By setting operator to or we define that at least one element of the collection has to pass the test.

There exists an instanceof operator which we could use to check against org.eclipse.core.resources.IProject. Unfortunately not all projects implement that interface. Instead they use the adapter framework. Therefore we use the adapt operator to see if the element can be adapted to an IProject. Of course this works for all classes implementing the interface directly too.


Test 3: Enable when exactly 2 txt Files are selected


Now for something more complex. A button shall be visible when exactly two resources are selected and both have an extension of .txt.
            <command
                  commandId="org.eclipse.ui.file.exit"
                  label="Test3"
                  style="push">
               <visibleWhen
                     checkEnabled="false">
                  <with
                        variable="selection">
                     <and>
                        <iterate
                              operator="and">
                           <test
                                 property="org.eclipse.core.resources.extension "
                                 value="txt">
                           </test>
                        </iterate>
                        <count
                              value="2">
                        </count>
                     </and>
                  </with>
               </visibleWhen>
            </command>
The property tester for file extensions is described in the documentation. The count operator counts the elements within a collection.


Test 4: Login/Logout

Let's add two more buttons for a very basic user login.
   <extension
         point="org.eclipse.ui.menus">
      <menuContribution
            allPopups="false"
            locationURI="menu:file?after=additions">
         <command
               commandId="com.codeandme.coreexpressions.commands.login"
               label="Login"
               style="push">
            <visibleWhen
                  checkEnabled="false">
               <with
                     variable="com.codeandme.coreexpressions.currentStatus">
                  <or>
                     <equals
                           value="startup">
                     </equals>
                     <equals
                           value="logged off">
                     </equals>
                  </or>
               </with>
            </visibleWhen>
         </command>
         <command
               commandId="com.codeandme.coreexpressions.commands.logout"
               label="Logoff"
               style="push">
            <visibleWhen
                  checkEnabled="false">
               <with
                     variable="com.codeandme.coreexpressions.currentUser">
                  <equals
                        value="John Doe">
                  </equals>
               </with>
            </visibleWhen>
         </command>
      </menuContribution>
We use dedicated commands that will set/reset the current user of our source provider from the previous post. The different source providers for both buttons do not necessarily make sense - we could achieve the same with just one source. Its just to give you an idea how it works.


We use our custom property tester to verify the current logon state and the current user.

Source Provider, Property Tester and the Expressions Framework

The expressions framework provides powerful means to declare expressions via XML and query them at runtime. Such expressions can be used without loading the defining Plug-in. You can find them in command/menu enablements. There you find visibleWhen, enabledWhen, activeWhen expressions. But you also can use them for your own needs.

In this example we will create our own Source Provider with a custom property tester (don't worry, I will explain this right ahead).

There is good documentation available for further reading.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online. 

Introduction

The expression framework can create/evaluate boolean expressions - something that in the end evaluates to true or false. To evaluate we need an object to work with (a source) and operators which return a boolean value.

Eclipse already defines commonly used sources and operators.

A simple example of an expression looks like this:
<visibleWhen>
     <and>
        <systemTest
              property="os.name"
              value="Linux">
        </systemTest>
        <systemTest
              property="os.arch"
              value="i386">
        </systemTest>
     </and>
<visibleWhen>

and is a basic operator that - of course - performs a boolean and operation. systemTest queries a java system property and compares it to a given value. So this expression evaluates to true only if we are working on an i386 linux machine.

Our source is not explicitely defined in the expression as the operator queries a static source System.getProperties().

Step 1: Creating a SourceProvider

For most expressions we will need sources that contain modifiable content. Use the predefined sources to get the active editor, the active selection or similar workbench related items. In this tutorial we will create a custom source to denote the current user.

Create a new Plug-in Project called com.codeandme.coreexpressions. In the plugin.xml create a new extension for org.eclipse.ui.services and add a new sourceProvider to it. Create two new variables for the sourceProvider:
set name to com.codeandme.coreexpressions.currentStatus and com.codeandme.coreexpressions.currentUser and leave priorityLevel set to workbench.

Now implement the sourceProvider by creating a class com.codeandme.coreexpressions.ExampleSourceProvider:
package com.codeandme.coreexpressions;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.ui.AbstractSourceProvider;
import org.eclipse.ui.ISources;

public class ExampleSourceProvider extends AbstractSourceProvider {

 public static final String CURRENT_STATUS = "com.codeandme.coreexpressions.currentStatus";
 public static final String CURRENT_USER = "com.codeandme.coreexpressions.currentUser";

 private Object fUser = null;
 private String fStatus = "startup";

 public ExampleSourceProvider() {
 }

 @Override
 public void dispose() {
 }

 @Override
 public Map getCurrentState() {
  HashMap<String, Object> map = new HashMap<String, Object>();
  map.put(CURRENT_USER, fUser);
  map.put(CURRENT_STATUS, fStatus);

  return map;
 }

 @Override
 public String[] getProvidedSourceNames() {
  return new String[] { CURRENT_USER, CURRENT_STATUS };
 }
}
We need to implement two methods:
  • getProvidedSourceNames()
    returns an array of source identifiers (variables). These are the same we already defined as variables in the plugin.xml.
  • getCurrentState()
    returns a Map<String, Object> with entries for each defined variable.

Step 2: Creating a property tester

Eclipse provides a generic expression operator test which allows to register custom tests.

Create a new extension for org.eclipse.core.expressions.propertyTesters with a unique id.

The type reflects to the kind of objects you'd get from the source provider. For now leave it to java.lang.Object.

The identifier of a specific property to check should be a fully qualified name. It consists of a namespace part and a properties name. It is good practice to set the namespace to your Plug-in identifier. Therefore set namespace to com.codeandme.coreexpressions and add two specific properties name,validated to properties. Hence our properties are named:
  • com.codeandme.coreexpressions.name
  • com.codeandme.coreexpressions.validated
Finally implement the class com.codeandme.coreexpressions.ExamplePropertyTester:
package com.codeandme.coreexpressions;

import org.eclipse.core.expressions.PropertyTester;

public class ExamplePropertyTester extends PropertyTester {

 private static final String NAME = "name";
 private static final String VALIDATED = "validated";

 public ExamplePropertyTester() {
 }

 @Override
 public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
  if (NAME.equals(property))
   return receiver.toString().equals(expectedValue);

  if (VALIDATED.equals(property)) {
   // do some background checks to see if user is validated
   return true;
  }

  return false;
 }
}
The receiver of the test() implementation is some object from a source provider. If you set your property tester to only accept objects of a certain type you need to add an instanceof check in the test() method.

Step 3: Your own expression

To use our source provider and property tester we can either create an expression for some activeWhen, enabledWhen or visibleWhen statement or we can extend org.eclipse.core.expressions.definitions to store the expression for later usage.

Create a new extension for org.eclipse.core.expressions.definitions with a unique id. Add a with section to define which source to use. Set variable to the name of our currentUser source: com.codeandme.coreexpressions.currentUser.

Add a test section to define our custom test. Set property to com.codeandme.coreexpressions.name and value to a string to compare against. In the sample I use "John Doe".

Make sure you activate forcePluginActivation. This defines that the Plug-in defining the property tester should be activated. If it is not activated, the property test cannot be performed.

The final definition:
         <with
               variable="com.codeandme.coreexpressions.currentUser">
            <test
                  forcePluginActivation="true"
                  property="com.codeandme.coreexpressions.name"
                  value="John Doe">
            </test>
         </with>
Step 4: Updating your source

Expressions do work right now, but our source is some kind of static. It won't change its status. Open ExampleSourceProvider and add following code:
 public void setCurrentUser(Object user) {
  fUser = user;
  fStatus = (user == null) ? "logged off" : "logged on";

  fireSourceChanged(ISources.WORKBENCH, CURRENT_USER, fUser);
  fireSourceChanged(ISources.WORKBENCH, CURRENT_STATUS, fStatus);
 }
fireSourceChanged() will tell Eclipse that a source changed and therefore forces expressions using this source to be re-evaluated.

To get an instance of your source provider you can query the ISourceProviderService:
ISourceProviderService service =(ISourceProviderService) PlatformUI.getWorkbench().getService(ISourceProviderService.class);
ISourceProvider provider = service.getSourceProvider(ExampleSourceProvider.CURRENT_USER);

Custom services

When implementing RCP applications you sometimes might need to define your own services. For example this could be an abstraction of a database or a core instance providing resources. Often this is done by implementing a singleton which is directly accessed by consumers. Such an implementation does not provide a separation between a service definition and the actual implementation.

With eclipse services we can achieve exactly that with very little overhead. This article is based on the online documentation on services.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Creating the service definition

Start with a new Plug-in Project called com.codeandme.customservice. On the Extensions tab of your plugin.xml add a new extension to org.eclipse.ui.services.

Step 2: Creating the service

We want to declare our service through an interface so we could easily exchange the implementation. Therefore create a new interface com.codeandme.customservice.ICustomService. Declaration of specific methods depends of what your service should provide and is not important for this example.

For the implementation create a new class com.codeandme.customservice.CustomServiceImpl with following content:
package com.codeandme.customservice;

public class CustomServiceImpl implements ICustomService {

 private static CustomServiceImpl fInstance = null;

 public static CustomServiceImpl getInstance() {
  if (fInstance == null)
   fInstance = new CustomServiceImpl();

  return fInstance;
 }

 private CustomServiceImpl() {
 }
}
Go back to your plugin.xml and set the serviceClass to com.codeandme.customservice.ICustomService.

Step 3: Creating a service factory

Create a factory class com.codeandme.customservice.CustomServiceFactory:
package com.codeandme.customservice;

import org.eclipse.ui.services.AbstractServiceFactory;
import org.eclipse.ui.services.IServiceLocator;

public class CustomServiceFactory extends AbstractServiceFactory {

 public CustomServiceFactory() {
 }

 @Override
 public Object create(@SuppressWarnings("rawtypes") Class serviceInterface, IServiceLocator parentLocator, IServiceLocator locator) {
  if (serviceInterface.equals(ICustomService.class)) 
   return CustomServiceImpl.getInstance();

  return null;
 }
}
Head back to your plugin.xml and set factoryClass to com.codeandme.customservice.CustomServiceFactory. Our final service declaration looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.services">
      <serviceFactory
            factoryClass="com.codeandme.customservice.CustomServiceFactory">
         <service
               serviceClass="com.codeandme.customservice.ICustomService"></service>
      </serviceFactory>
   </extension>

</plugin>

Step 4: Using the service

When your application needs a service instance it can directly query the service locator with following snippet (from within a view):
ICustomService customService = (ICustomService) getSite().getService(ICustomService.class);
Or globally with:
ICustomService customService = (ICustomService) PlatformUI.getWorkbench().getService(ICustomService.class);
The source files for this article contain a separate Plug-in using the service.

Final thoughts

At first sight you don't gain a lot with this approach. Real world examples often would separate service definition and implementation into different Plug-ins. When the service is used through its interface and accessed through the ServiceLocator your clients do not have any dependency to the factory or the implementing class. This lets you easily exchange the implementation without altering the clients.

As found in the documentation a service factory might provide different services but for a dedicated serviceClass there may exist only one factory. Such a thing could be done by declaring your own extension points.

It might be a drawback that for services you need a dependency to UI Plug-ins.

Sunday, March 4, 2012

A new kernel for the dockstar

The dockstar is a small embedded system originally used as a small NAS system. With some effort it is possible to install linux on such a system. When running gentoo it might be necessary to update the kernel from time to time.

All we need is a kernel .config file and a short summary how to update.

Step 1: getting the kernel
emerge --sync
emerge gentoo-sources
Will retrieve the newest sources. As kernel updates will happen rarely you might want to mask newer kernels from portage. Do this by adding following line to /etc/portage/package.mask.
>sys-kernel/gentoo-sources-3.1.10-r1
Step 2: Kernel configuration

Switch to your new kernel:
eselect kernel list
eselect kernel set <number>
Now you could either download one from the config files below or config the kernel on your own.
cd /usr/src/linux
make menuconfig
After configuration you should have a .config file in your linux directory.

Step 3: Build the kernel
make clean uImage modules modules_install
Step 4: Install the kernel

As you might have a running kernel already its best to backup this version first:
mount /boot
mv /boot/uImage /boot/uImage.bak
If your new kernel does not boot you can revert to the old kernel on another system.
cp /usr/src/linux/arch/arm/boot/uImage /boot
Now cross your fingers and reboot the system.

Files:

Thursday, February 23, 2012

Creating a headless application

There are times when you might want to run your RCP application in headless mode. While this is typically not the default mode for applications it might still be useful as an alternative launch option. Eclipse comes with a wizard for this. let' see how it works.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online. 


Step 1: Create the Application

Create a new Plug-in Project called com.codeandme.headless. Eclipse allows to register applications, classes that implement IApplication. At startup eclipse allows to launch exactly one of these classes which is the main entry point to your application.

Lets build such an application: first lets create the extension point for it.
Therefore open the MANIFEST.MF file and switch to the Extensions tab. Add a new org.eclipse.core.runtime.applications extension. Create a new run subnode and set the class name to com.codeandme.headless.Application. The ID of our application will be comprised of the plugin name followed by the ID of the root node of the extension. So lets set the ID to application. The final plugin.xml should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>

   <extension
         id="application"
         point="org.eclipse.core.runtime.applications">
      <application
            cardinality="singleton-global"
            thread="main"
            visible="true">
         <run
               class="com.codeandme.headless.Application">
         </run>
      </application>
   </extension>
</plugin>
Clicking on the class link creates the java class:
package com.codeandme.headless;

import java.util.Map;

import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;

/**
 * This class controls all aspects of the application's execution
 */
public class Application implements IApplication {

 @Override
 public Object start(final IApplicationContext context) throws Exception {
  System.out.println("Hello RCP World!");
  final Map<?,?> args = context.getArguments();
  final String[] appArgs = (String[]) args.get("application.args");
  for (final String arg : appArgs) {
   System.out.println(arg);
  }

  return IApplication.EXIT_OK;
 }

 @Override
 public void stop() {
  // nothing to do
 }
}

Instead of a java main() we now have to implement start() and stop(). We are not interested in UI here, so we simply print some messages to the console and finish.

Step 2: A simple test run

Open the Launch Configurations and add a new Eclipse Application launch. On the main tab activate Run an application and select com.codeandme.headless.application. Make sure all required plug-ins are selected and give it a try. You should get some print outputs on the console.


Step 3: Create a Product Configuration

We will create a very basic product configuration here just to get a working runnable.

Select our Plug-in com.codeandme.headless, and select New -> Product Configuration from the context menu. Set the File name to "Headless" and select "Create a configuration file with basic settings".



On the Overview tab find the section Product Definition and hit the New... button on the righthand side of the Product line.

Enter a nice Product Name and a unique Product ID. Make sure the Application is set to com.codeandme.headless.application. The Defining Plug-in needs to be part of your exported RCP, in our case we will simply use com.codeandme.headless for this purpose.


Switch over to the Contents tab and add com.codeandme.headless. Further add the plugin org.eclipse.core.runtime, then select Add Required Plug-ins to automatically resolve all dependencies. Save your Product Configuration and test it by selecting Launch an Eclipse application on the Overview tab. Again you should see the output on the Console view.

Step 4: Exporting the RCP Product

Start the Eclipse Product export wizard from the Overview tab of the Product Configuration. Just select an appropriate destination Directory and let the wizard do the rest. In case you do this multiple times it might help to manually wipe the destination directory before starting a new export.

This way of building products is fine for a test run, if you are interested in a more  professional way of building products, have a look at my tycho tutorials.

Step 5: Running the Application

To run the application just double click eclipse.exe in the destination directory. Still this won't give you any output. So fire up a console and enter

eclipse -application com.codeandme.headless.application -consoleLog -noExit first second last
  • -application
    will tell eclipse which application to start. You might omit this if you want to run the default application you exported. It makes sense for RCPs with multiple applications though.
  • -consoleLog
    opens a dedicated eclipse console and should be well known already.
  • -noExit
    will keep eclipse running even if our application finished. It keeps the console open to examine output 
The other parameters are passed to our application and therefore will be parsed and printed.