Running Rational Team Concert (Jazz) on Eclipse 3.5 / Mac OS X

Rational Team Concert 2.0 has been released. With a little work, you can get the client running on Eclipse 3.5 and the server running on Mac OS X.

Server

The Jazz team does not officially support running the server on OS X, and a download that runs on OS X out-of-the-box is not available.  With a couple of minor modifications, the Linux server download will run on OS X just fine.

  1. Download the Express C Server for Linux.
  2. Unzip the download
  3. Launch the Terminal
  4. cd jazz/server
  5. rm -rf jre (the Linux JRE is obviously not needed on OS X)
  6. Edit tomcat/conf/server.xml.  You need to modify the Connector specification for port 9443.  Search for SSL_TLS and change it to TLS.  Also change the next line from IbmX509 to SunX509.  The resulting Connector specification should look like:
  7. <Connector port="9443"
                   connectionTimeout="20000"
                   maxHttpHeaderSize="8192"
                   maxThreads="150"
                   minSpareThreads="25"
                   maxSpareThreads="75"
                   enableLookups="false"
                   disableUploadTimeout="true"
                   acceptCount="100"
                   scheme="https"
                   secure="true"
                   clientAuth="false"
                   keystoreFile="ibm-team-ssl.keystore"
                   keystorePass="ibm-team"
                   sslProtocol="TLS"
    			   algorithm="SunX509"
    			   URIEncoding="UTF-8" />
    
  8. Edit server.startup and server.shutdown changing JRE_HOME from `pwd`/jre to /System/Library/Frameworks/JavaVM.framework/Home
  9. While editing server.startup add -XX:MaxPermSize=256m to JAVA_OPTS
  10. Launch the server ./server.startup

If you want to make sure the server started correctly, you can tail -f tomcat/logs/catalina.out and watch the server startup.  If you see any exceptions thrown, something went wrong.  If all goes well, the last message you see in the log should be:

INFO: Server startup in xxxxx ms

To get started right away, launch Safari and point it to https://localhost:9443/jazz login with User: ADMIN and Password: ADMIN.  You can use this admin account to create a personalized account.  Don’t forget to give yourself a Developer license.

Client

These instructions are not specific to Mac OS X – they should work for any OS.

The Jazz team does support a Mac OS X Client as an incubator project, but the client is based on Eclipse 3.4.  You can download the client and get the jazz bundles to work in an Eclipse 3.5 environment.   The Jazz client has dependencies on the following features:

With the exception of SDO, all of the features can be installed from the Galileo update site.  To get SDO, add the EMF releases update site and install the SDO runtime from the EMF 2.4.2 release.

From the downloaded client, move all of the sub-folders in jazz/client/eclipse/jazz to your Eclipse 3.5 dropins folder.  You could use P2 to install the contents of those sub-folders; however, you must manually check every bundle to be installed.  I’d rather spend 10 seconds doing a drag-and-drop rather than 15 minutes checking checkboxes.

After you launch the client, you should be able to open the Team Artifacts view and create a connection to the repository at https://localhost:9443/jazz

Next Steps

See the Jazz web site for tutorials on getting started.

Update: Added -XX:MaxPermSize=256m to the server instructions

Remote declarative OSGi services

My last post on OSGi services showed how you can dynamically configure OSGi services using the ConfigurationAdmin service.  I used an example where I configured a service to create a JDBC connection.  JDBC connections are often made on a server, and it would be nice to be able to do your configuration remotely.  This post will show you how you can use RFC 119 to make remote OSGi service calls to configure the JDBC connection on a server.

There are two implementations of RFC 119: Apache CXF, and ECF.  I’m using ECF for this example.  This example also uses Bonjour (Zeroconf) for service discovery.  This means that the client and server must be running on the same local subnet for the client to discover the services advertised by the server.  ECF also supports SLP for service discovery, but like Bonjour is limited to the local subnet.  ECF allows you to plug in your own discovery provider, and I have done just that to support discovery across a WAN.  Look for a future blog post describing how to create your own ECF discovery provider.

Service Interface

Lets start by creating our remote service interface:

  1. Create a new plug-in project com.example.database.admin
  2. Create a new interface IDatabaseAdminService
package com.example.database.admin;

import java.util.Dictionary;

public interface IDatabaseAdminService
{
	public void createConnection(Dictionary<String, String> connectionConfig);
}

Service Implementation

Create an implementation of IDatabaseAdminService. The implementation creates a Configuration using the ConfigurationAdmin service.

package com.example.database;

import java.io.IOException;
import java.util.Dictionary;

import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;

import com.example.database.admin.IDatabaseAdminService;

public class DatabaseAdminService implements IDatabaseAdminService
{
	public synchronized void createConnection(Dictionary<String, String> connectionConfig)
	{
		try
		{
			Configuration config = null;

			if (configAdmin != null)
				config = configAdmin.createFactoryConfiguration("com.example.database.connection", null);

			if (config != null)
				config.update(connectionConfig);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	protected synchronized void bind(ConfigurationAdmin configAdmin)
	{
		this.configAdmin = configAdmin;
	}

	protected synchronized void unbind(ConfigurationAdmin configAdmin)
	{
		if (this.configAdmin == configAdmin)
			this.configAdmin = null;
	}

	private ConfigurationAdmin configAdmin;
}

Declare the Admin Service

Now that we have a service, let’s declare it:

  1. Create a new Component Definition
  2. Set File name: admin.xml
  3. Set name: com.example.database.admin
  4. Set Class: com.example.database.DatabaseAdminService

Using the Component Definition editor, we need to set a service property osgi.remote.interfaces=*.  This property tells the RFC 119 implementation to make all declared interfaces available remotely.

Component Overview

Next, on the services tab, add a dependency on the ConfigurationAdmin service, and declare the IDatabaseAdminService.  Don’t for get to set the bind and unbind functions on the referenced ConfigurationAdmin service.

Component Services

Initialize the ECF Container

ECF does much more than implement RFC 119, so in order to use distributed services, you must create an instance of the ecf.r_osgi.peer container.  This should be done in its own bundle since it is necessary on both the client and server.

  1. Create a new Plug-in Project
  2. Create an Activator for the bundle
package com.example.services.init;

import org.eclipse.ecf.core.ContainerCreateException;
import org.eclipse.ecf.core.IContainer;
import org.eclipse.ecf.core.IContainerManager;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

/**
 * The activator class controls the plug-in life cycle
 */
public class Activator implements BundleActivator
{
	public void start(final BundleContext context) throws Exception
	{
		containerManagerTracker = new ServiceTracker(context, IContainerManager.class.getName(), new ServiceTrackerCustomizer()
		{
			public void removedService(ServiceReference reference, Object service)
			{
				if (container != null)
					container.disconnect();

				container = null;
			}

			public void modifiedService(ServiceReference reference, Object service)
			{}

			public Object addingService(ServiceReference reference)
			{
				try
				{
					IContainerManager containerManager = (IContainerManager) context.getService(reference);
					container = containerManager.getContainerFactory().createContainer("ecf.r_osgi.peer");
					return containerManager;
				}
				catch (ContainerCreateException e)
				{
					e.printStackTrace();
					return null;
				}
			}
		});

		containerManagerTracker.open();
	}

	public void stop(BundleContext context) throws Exception
	{
		containerManagerTracker.close();

		if (container != null)
			container.dispose();
	}

	private ServiceTracker containerManagerTracker;
	private IContainer container;
}

Launch Server

You can launch the server now, or wait until you have written the client code.  To launch the server:

  1. Create a new OSGi Framework launch configuration
  2. Click Deselect All
  3. Uncheck Include optional dependencies when computing required bundles
  4. Uncheck Add new workspace bundles to this launch configuratin automatically
  5. Check com.example.database
  6. Check com.example.database.admin
  7. Check com.example.services.init
  8. Check ch.ethz.iks.r_osgi.remote
  9. Check org.eclipse.ecf.discovery
  10. Check org.eclipse.ecf.osgi.services
  11. Check org.eclipse.ecf.osgi.services.discovery
  12. Check org.eclipse.ecf.osgi.services.distribution
  13. Check org.eclipse.ecf.provider
  14. Check org.eclipse.ecf.provider.jmdns
  15. Check org.eclipse.ecf.provider.r_osgi
  16. Check org.eclipse.ecf.remoteservice
  17. Check org.eclipse.equinox.cm
  18. Check org.eclipse.equinox.ds
  19. Click Add Required Bundles
  20. Set the Start Level of org.eclipse.equinox.ds to 5
  21. Set Auto-Start to true on org.eclipse.ecf.osgi.*
  22. Set Auto-Start to true on org.eclipse.ecf.provider.jmdns
  23. Set Auto-Start to true on org.eclipse.equinox.cm
  24. Set Auto-Start to true on org.eclipse.equinox.ds

You normally should not set the start level of org.eclipse.equinox.ds.  However, there is a start order dependency bug in ECF that requires your services to be registered after the ecf.r_osgi.peer container, and a bug in declarative services that requires DS to start after CM, so the easiest workaround is to set the start level of DS to 5 with a default start level of 4.  Your launch configuration should look something like:

Launch Config

After you launch the server, issue the command services (objectClass=com.example.database.admin.IDatabaseService).  You should see the service listed twice.  Note that there are not really two instances of the service running.  One is a proxy created by ECF to handle the remote calls.

Registered Services

Client

Now we need to create the client code that will call the remote service.  To illustrate the power of RFC 119, let’s create the client code as a declared service itself that dynamically binds to the remote service.

  1. Create a new plug-in project com.example.database.client
  2. Check Activate this plug-in when one of its classes is loaded
  3. Create a new folder OSGI-INF
  4. Create a new Component Definition
  5. Create a new ClientAdmin class
  6. Declare a dependency on IDatabaseAdminService

Client Component

Don’t forget to specify the bind and unbind functions for the referenced service.

Client Referenced Services

Here is the AdminClient:

package com.example.database.client;

import java.util.Hashtable;

import org.osgi.service.component.ComponentContext;

import com.example.database.admin.IDatabaseAdminService;

public class AdminClient
{
	protected synchronized void activate(ComponentContext context)
	{
		if(adminService == null)
			return;

		Hashtable<String, String> properties = new Hashtable<String, String>();
		properties.put("database.id", "example");
		properties.put("database", "junit");
		properties.put("user", "");
		properties.put("password", "");
		properties.put("create", "create");

		adminService.createConnection(properties);
		System.out.println("Database connection created");
	}

	protected synchronized void bind(IDatabaseAdminService adminService)
	{
		this.adminService = adminService;
	}

	protected synchronized void unbind(IDatabaseAdminService adminService)
	{
		if(this.adminService == adminService)
			this.adminService = null;
	}

	private IDatabaseAdminService adminService;
}

Launch Client

We can now launch the client:

  1. Clone the Server launch configuration
  2. Uncheck com.example.database
  3. Check com.example.database.client

Client Launch

After launching the client and seeing “Database connection created” print to the console, switch over to the server console and type: services (objectClass=com.example.database.IDatabaseConnectionService) and you will see that the service was configured by the client and started.

Service Configured

What has happened here?  When the client started, ECF discovered the service advertised by the server and registered a proxy as if it were a local OSGi service.  The declarative services bundle saw the service and recognized that it was required by the client component.  The service proxy was dynamically bound to the client component by calling bind(IDatabaseAdminService).  The client service was then activated by the declarative services bundle by calling activate(ComponentContext).  The client then created a configuration and called IDatabaseService.createConnection(Dictionary<String, String>) which made a remote call to the service running on the server.  The server side service created a Configuration using the ConfigurationAdmin service.  The configuration of the service then caused the declarative services bundle on the server to create an instance of the IDatabaseConnectionService.  Pretty cool!

Update: fixed a cut/paste problem with the source code.

Update 8/23/09: There is a bug in ECF 3.0 that prevents more than one remote service from being discovered.  You may still use ECF 3.0, but will need to check out org.eclipse.ecf.provider.r_osgi from CVS to pick up the fix.  This fix will be released in ECF 3.1

Modeling SWT with EMF

My EMF Workflow Model is starting to take shape.  One of the next features I’d like to add is to make it easier to create and configure a workflow.  I was thinking it would be nice if the configuration editor was configurable without having to write a lot (if any) code.

I started playing around with the idea of modeling SWT with EMF, and it actually seems to work nicely.  I’ve modeled about half of the SWT widgets and all of the layouts in EMF.  This screencast shows an Eclipse view in which the contents are created from an EMF model, then I change the model and refresh the view to reflect the updated model.  The next step will be to figure out how to model databindings and connect them to a EMF Workflow Model.

Catch the Wave

If you haven’t seen the Google Wave announcement, you need to crawl out of your cave and watch the demo.  This is by far the most impressive communication and collaboration tool I have ever seen.  Say good-bye to email, instant messaging, newsgroups, forums, wikis, and blogs as we know them.  All of these technologies are fundamentally a way to communicate and collaborate with others, and all have their strengths and weaknesses.  Wave seems to take all of the strengths of these tools and combines them into a seamless system.

One Wave to rule them all, One Wave to find them, One Wave to bring them all and in the darkness bind them.

Two questions came to mind as I watched the demo in awe:

  1. How can the Eclipse community use wave to better collaborate?
  2. How can we integrate Wave into Eclipse?

Imagine if the Eclipse documentation were a collaborative wave document.  Developers could contribute additional documentation, code examples, etc, and that information would be updated live for other developers.  Questions on newsgroups could be easily incorporated into the documentation, or into a FAQ.  Bugs could be opened from a newsgroup question by simply highlighting text and clicking a button.  I think the Wave protocols should be integrated into ECF, and Eclipse views and editors could be used to display and edit the Wave content.  The possibilities seem endless.

Most of all, I’d like to see some lively discussion by the Eclipse community on your ideas.

Dynamically configured declarative OSGi services

I’m creating OSGi services and found the need to create one or more instances of a service that was registered with user configured parameters.  It’s fairly straightforward to do this with the ConfigurationAdmin service, a ManagedServiceFactory and context.registerService(), but I’ve recently become a declarative services convert and wanted to do all of this with declarative services.  It turns out that you still use the ConfigurationAdmin service, just not the ManagedServiceFactory. In this post, I will demonstrate how to create a dynamically configured declarative service.

Example Use-case

Let’s say you are deploying an OSGi based server at multiple sites and your bundles need to access one or more databases using JDBC.  You create an OSGi service called DatabaseConnectionService that returns a JDBC Connection.  To create the connection to the database, you need configuration information typically consisting of: hostname, port number, database, user id, and password.  Since this information can change, and will vary from site to site where your server is deployed, the configuration information needs to be dynamically updatable and persistable.

Development Environment

You will need Eclipse 3.5, Equinox, and Apache Derby if you want to try this example on your own.  Create a target folder somewhere in your filesystem.  Extract the contents of the Equinox zip file into the target directory.  Create a derby folder as a child of target and extract the contents of the Derby zip into the derby folder.  The directory structure should look like:

Target platform directory structure

Target platform directory structure

Launch Eclipse 3.5 and select Preferences -> Plug-in Development -> Target Platform.  We need to add Equinox and Derby to our target platform.  Select Running Platform and click Edit… Add the Equinox and Derby folders to your target platform.  Your target platform should look like:

Target Definition

Target Definition

Unit Test

Create a new plug-in project and make sure the option is checked to generate an Activator.  In the Manifest editor, check Activate this plug-in when one of its classes is loaded.  Add org.junit4 to your required bundles and org.osgi.service.cm to your imported packages.  [Let’s not debate require bundle vs import package … just set up your dependencies however you like to get the junit tests to build.]

The unit test first gets the ConfigurationAdmin service from the bundle context.  I’ve taken a short-cut with my unit test for this example, and I’m relying on the fact that the ConfigurationAdmin service will be started before the unit test.  You should not rely on bundle start order in your production code.  Once you have the ConfigurationAdmin service, create a configuration and set the properties for your declarative service.  After you update the properties on the configuration, you will then have to wait for the service to be created.  Make sure you include a timeout so your unit test doesn’t hang forever in the event that the service is not started.  Your unit test will contain a compile error until you create the service interface.

package com.example.database.junit;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;

import org.junit.Test;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.util.tracker.ServiceTracker;

import com.example.database.IDatabaseConnectionService;

public class TestDatabaseConnectionService
{
	@Test
	public void testCreateConnection() throws SQLException, IOException, InvalidSyntaxException, InterruptedException
	{
		BundleContext context = Activator.getDefault().getBundle().getBundleContext();

		ServiceReference configAdminReference = context.getServiceReference(ConfigurationAdmin.class.getName());
		assertThat(configAdminReference, is(notNullValue()));
		ConfigurationAdmin configAdmin = (ConfigurationAdmin) context.getService(configAdminReference);
		assertThat(configAdmin, is(notNullValue()));

		Configuration config = configAdmin.createFactoryConfiguration("com.example.database.connection", null);
		assertThat(config, is(notNullValue()));

		Hashtable<String, Object> properties = new Hashtable<String, Object>();
		properties.put("database.id", "example");
		properties.put("database", "junit");
		properties.put("user", "");
		properties.put("password", "");
		properties.put("create", "create");
		config.update(properties);

		ServiceTracker connectionServiceTracker = new ServiceTracker(context, IDatabaseConnectionService.class.getName(), null);
		connectionServiceTracker.open();
		IDatabaseConnectionService connectionService = (IDatabaseConnectionService) connectionServiceTracker.waitForService(1000);
		assertThat(connectionService, is(notNullValue()));
		Connection connection = connectionService.createConnection();
		assertThat(connection, is(notNullValue()));
		connection.close();
	}
}

Service Interface

Create a new plug-in project com.example.database and add the following interface:

package com.example.database;

import java.sql.Connection;
import java.sql.SQLException;

public interface IDatabaseConnectionService
{
	Connection createConnection() throws SQLException;
}

Edit your junit plug-in manifest to include a dependency on com.example.database.

Launch Configuration

Create a JUnit Plug-in Test launch configuration.  Make sure your test is selected, and that the Test runner is set to JUnit 4.

Launch Configuration (Test tab)

Launch Configuration (Test tab)

You will also need to set up the included plug-ins.

  1. Select the Plug-ins tab
  2. Select Launch with: plug-ins selected below only
  3. Click Deselect All
  4. Uncheck Include optional dependencies when computing required plug-ins
  5. Uncheck Add new workspace plug-ins to this launch configuration automatically
  6. Check com.example.database
  7. Check com.example.database.junit
  8. Check org.eclipse.equinox.cm
  9. Check org.eclipse.equinox.ds
  10. Click Add Required Plug-ins
  11. Set the Start Level on org.eclipse.equinox.cm to 3 and Auto Start to true

Launch Configuration (Plug-ins tab)

Launch Configuration (Plug-ins tab)

The start level of the ConfigurationAdmin bundle must be set to something lower than the declarative services bundle because of bug 276003.  Run your junit test, and it should fail on line 62.

Service Implementation

Now, lets create a real service that constructs a JDBC connection to derby.

package com.example.database;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Dictionary;

import org.apache.derby.jdbc.EmbeddedDataSource;
import org.osgi.service.component.ComponentContext;

public class DatabaseConnectionService implements IDatabaseConnectionService
{
	public Connection createConnection() throws SQLException
	{
		EmbeddedDataSource dataSource = new EmbeddedDataSource();
		dataSource.setDataSourceName(id);
		dataSource.setDatabaseName(database);
		dataSource.setUser(user);
		dataSource.setPassword(password);
		dataSource.setCreateDatabase(create);
		return dataSource.getConnection();
	}

	@SuppressWarnings("unchecked")
	protected void activate(ComponentContext context)
	{
		Dictionary<String, Object> properties = context.getProperties();
		id = (String) properties.get("database.id");
		database = (String) properties.get("database");
		user = (String) properties.get("user");
		password = (String) properties.get("password");
		create = (String) properties.get("create");
	}

	private String id;
	private String database;
	private String user;
	private String password;
	private String create;
}

Declare The Service

You are now ready to declare your service. Eclipse 3.5 contains new tooling for declarative services that makes this a snap.

  1. Right-click on your project and select New -> Folder
  2. Create a folder called OSGI-INF
  3. Right-click on the OSGI-INF folder and select New -> Other
  4. Select Plug-in Development -> Component Declaration
  5. Click Next
  6. Choose a file name such as component.xml
  7. Give your component a globally unique name such as com.example.database.connection
  8. For the Class, click Browse
  9. Choose DatabaseConnectionService
  10. Click Finish

The component definition editor will appear allowing you to set additional parameters and options. Set the Configuration Policy to require. This policy tells the service manager to not start the service component until it has a configuration from the ConfigurationAdmin service.  Check the options: This component is enabled when started and This component is immediately activated.

Component Options

Component Options

On the Services tab of the component editor, add the service interface as a provided service:

Component Services

Component Services

If you re-run your junit test, it should now pass.  If you want to dig a little deeper, set a breakpoint on line 55 of TestDatabaseConnectionService.java and run under the debugger.  When your breakpoint is triggered, switch to the console view and type in services (objectClass=com.example.database.IDatabaseConnectionService) and you will get information on your registered service including it’s configuration parameters.

OSGi Console

OSGi Console

That’s how you dynamically configure a declarative service!

Update: Simplified the unit test by using ServiceTracker.waitForService().