Jazz Library Hello Jazz – How to write a simple Jazz component
Author name

Hello Jazz — How to write a simple Jazz component

This tutorial walks you through the process of creating a very simple Jazz™ component with both client and server contributions.  As a prerequisite for this tutorial, you should be familiar with Eclipse plug-in development.

Overview of the Process

We will develop a Jazz component with a single service method that returns the string “Hello, Jazz!” from the server to the client. This component will consist of three plug-ins (common, client library and service), plus a JUnit test plug-in.

These are the basic steps we’ll take to create the component:

  1. Create the common plug-in, defining the Hello Jazz service interface.
  2. Create the client library plug-in, exposing our service to clients.
  3. Create the test plug-in, running against the client library. The test we develop will fail until we implement the service.
  4. Create the service plug-in, implementing our Hello Jazz service. Our test now passes.

The completed plug-ins developed during this tutorial can be found in the file com.example.hellojazz-plugins-source.zip.

Setup Steps

Before you begin the tutorial you will need to setup an environment for Jazz component development. Refer to the setup topic on the jazz.net wiki for instructions on how to do this.

Notes for use with Rational Team Concert 2.0

The setup steps on the wiki referenced above have been updated with notes for RTC 2.0. One difference relevant to this topic is that you will need to create separate workspaces for the client and server aspects of the code.

The server workspace should contain these projects:
  • com.example.hellojazz.common
  • com.example.hellojazz.service
The client workspace should contain these projects:
  • com.example.hellojazz.common
  • com.example.hellojazz.client
  • com.example.hellojazz.client.tests
  • com.example.hellojazz.rcp.ui

Note that the com.example.hellojazz.common will need to be included in both client and server workspaces. With this setup the 2.0 launch configuration attached to the JazzServerRunAndDebug wiki can be used to run the server from the server workspace (add the two server side plugins to this launch before starting it).

Additionally, the client launches for the Junit test and the Eclipse IDE will not be able to use the -Dcom.ibm.team.core.tests.repo=local argument. If you are using the launches from the code download, remove this argument. Finally, the JUnit test code will need to be modified slightly to refer to the server running remotely from the server workspace. Find the line that defines REPOSITORY_URI and change the value of this string to https://localhost:9443/jazz

Define the Service Interface

Jazz components typically have a common plug-in that contains interfaces, classes, EMF models, and other resources needed by both the client and service plug-ins.

Create the Common Plug-in

Create Plug-in Project com.example.hellojazz.common, in which we’ll define the service interface for our component.

New Project Wizard, Step 1

Specify the name of the project (com.example.hellojazz.common), and the target environment (Eclipse).

New Project Wizard, Step 2

Disable the creation of the activator, and click Finish to create the plug-in.

New Project Wizard, Step 3

Setup the Plug-in Dependencies

Our Hello Jazz common plug-in will depend on the common plug-in of the Jazz Repository component. Open the manifest of our new plug-in, select the Dependencies tab, and add com.ibm.team.repository.common to the list of required plug-ins:

Depend on com.ibm.team.repository.common

Declare the Hello Jazz Component

We need to declare our component by extending an extension point defined by com.ibm.team.repository.common. In the manifest editor, switch to the Extensions tab. Extend the com.ibm.team.repository.common.components extension point by clicking the Add… button and double clicking the extension point in the dialog:

Select components extension point
Note: As the image shows, you may see two instances of the extension-point. You can select either one.

Now fill in our component’s details:

Add detail to components extension

Create the Service Interface

Now let’s create the IHelloJazzService interface, defining the methods that the service plug-in will provide to the client plug-in:

Create service interface

And add a sayHello method to the new interface:

package com.example.hellojazz.common.service;

import com.ibm.team.repository.common.TeamRepositoryException;

public interface IHelloJazzService {
String sayHello() throws TeamRepositoryException;
}

Calls to sayHello might fail, for example because the user isn’t logged in, or because the service isn’t installed on the server. To plan for these failures, we declare sayHello to throw TeamRepositoryException.

Declare the Service Interface

Now that we have the interface defined, we can tell Jazz about it by adding more information to our extension of the com.ibm.team.repository.common.components extension point:

Create service element

Set the details of the service element as shown below:

Set service details

We need to export the service package in order for the client and service plug-ins to see it. Switch to the Runtime tab and add the com.example.hellojazz.common.service package:

TEXT

Client Library

Create the Client Library Plug-in

Each component providing client-side services does so with a client library plug-in. Create a plug-in named com.example.hellojazz.client. As before, you can disable the options in the wizard for generating an activator and making contributions to the UI.

Setup the Client Plug-in Dependencies

Our Hello Jazz client library plug-in will depend on the common plug-in we defined above, and also on the common and client plug-ins of the Jazz Repository component.

In the manifest editor, switch to the Dependencies tab and add these required plug-ins:

  • com.ibm.team.repository.common
  • com.ibm.team.repository.client
  • com.example.hellojazz.common

Create the Client Interface

Now let’s create the IHelloJazzClientLibrary interface, defining the methods that the client library will provide to clients:

package com.example.hellojazz.client;

import com.ibm.team.repository.common.TeamRepositoryException;

public interface IHelloJazzClientLibrary {
String sayHello() throws TeamRepositoryException;
}

For our simple service, it happens that this client library interface’s sole method is identical to that of the service. More sophisticated client libraries may not have such a direct mapping between client library methods and service methods. For example, there can be methods dealing with client-side state (i.e. which don’t need to invoke service methods), and methods which invoke multiple service methods (though this should be kept to a minimum as it implies multiple round-trips to the server).

Export and Declare the Client Library Interface

We need to export the client package in order for other plug-ins to see it:
exporting the client library package

Now let’s tell Jazz about this interface by extending the com.ibm.team.repository.client.clientLibraryFactory extension point. This will enable the Jazz client infrastructure to locate the interface and to create an instance of its implementation.

In the manifest editor, switch to the Extensions tab, click Add, and double click com.ibm.team.repository.client.clientLibraryFactory. This should add the extension and a factory element. Set the name to Hello Jazz Factory and interface class to com.example.hellojazz.client.IHelloJazzClientLibrary, the interface we created above.

Set factory details

Next click on factoryClass*: and create HelloJazzClientLibraryFactory, the class that Jazz will call when an implementation of IHelloJazzClientLibrary is needed:

Create client library factory

Note that we set the package to com.example.hellojazz.client.internal, a package that we won’t export to clients. The abstract base class, ClientLibraryFactory, declares an abstract method createClientLibrary that is called when an instance of a client library is needed. Implement the method as follows:

    @Override
public Object createClientLibrary(IClientLibraryContext context) {
return new HelloJazzClientLibrary(context);
}

Create the Client Library Implementation

Let’s implement the client library class. First we need the constructor:

package com.example.hellojazz.client.internal;

import com.example.hellojazz.client.IHelloJazzClientLibrary;
import com.example.hellojazz.common.service.IHelloJazzService;
import com.ibm.team.repository.client.util.IClientLibraryContext;
import com.ibm.team.repository.common.TeamRepositoryException;

public class HelloJazzClientLibrary implements IHelloJazzClientLibrary {
/**
* The client library context, which identifies the repository,
* and provides access to services.
*/
private final IClientLibraryContext context;

/**
* Creates the {@link IHelloJazzClientLibrary} implementation.
*
* @param context
* The client library context. Must not be null.
*/
HelloJazzClientLibrary(IClientLibraryContext context) {
if (context == null) {
throw new IllegalArgumentException(
"context must not be null");
}
this.context = context;
}

Next we need a private method that will return an implementation of IHelloJazzService. On the client the implementation is a proxy generated by calling IClientLibraryContext.getServiceInterface(Class cls). This proxy will forward method calls to the service implementation on the server.

    /**
* Returns the {@link IHelloJazzService} implementation.
* @return Returns an {@link IHelloJazzService} implementation.
* Never null.
* @exception TeamRepositoryException
* Thrown if the {@link IHelloJazzService} cannot be
* retrieved.
*/
private IHelloJazzService getService()
throws TeamRepositoryException {
IHelloJazzService service = (IHelloJazzService)
context.getServiceInterface(IHelloJazzService.class);
if (service == null) {
throw new TeamRepositoryException(
"Unable to get IHelloJazzService");
}
return service;
}

We’re now ready to invoke the service:

    /**
* Calls the IHelloJazzService.sayHello method on the server, and
* returns its result.
*
* @return The string returned from the server.
* @exception TeamRepositoryException
* Thrown if something goes wrong.
*/
public String sayHello() throws TeamRepositoryException {
String result = getService().sayHello();
return result;
}
} // End of the class

Create the Test Plug-in

Let’s create a JUnit plug-in test that exercises our service. It will initially fail because we’ve not yet created the server-side implementation. Start by creating a new plug-in named com.example.hellojazz.client.tests. As before, you can disable the options in the wizard for generating an activator and making contributions to the UI.

Setup the Plug-in Dependencies

Add the plug-in dependencies we’ll need in the Dependencies tab of the manifest editor:

  • org.junit
  • org.eclipse.core.runtime
  • com.ibm.team.repository.common
  • com.ibm.team.repository.client
  • com.example.hellojazz.client

Create a test class, TestHelloJazz, extending the JUnit TestCase class.  The test case must be logged in to a Jazz Repository to obtain and call our service, so the JUnit setUp() method calls a login() method to perform the login.  The test method, testSayHello(), obtains a handle to the client libary and then calls the sayHello() service method.

package com.example.hellojazz.client.tests;

import junit.framework.TestCase;

import com.example.hellojazz.client.IHelloJazzClientLibrary;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.client.TeamPlatform;
import com.ibm.team.repository.common.TeamRepositoryException;

public class TestHelloJazz extends TestCase {

private ITeamRepository repo;

protected void setUp() throws Exception {
super.setUp();

if (!TeamPlatform.isStarted())
TeamPlatform.startup();

repo = login();
}

protected void tearDown() throws Exception {
repo.logout(null);
assertFalse(repo.loggedIn());
repo = null;

super.tearDown();
}

public void testSayHello() throws TeamRepositoryException {
IHelloJazzClientLibrary library = (IHelloJazzClientLibrary) repo
.getClientLibrary(IHelloJazzClientLibrary.class);
assertNotNull(library);
String result = library.sayHello();
assertNotNull(result);
return;
}

/**
* The URI scheme <code>"local:"</code> creates a Jazz server on the same
* thread as the client.
*/
private static final String REPOSITORY_URI = "local:";

private ITeamRepository login() throws TeamRepositoryException {
ITeamRepository repository = TeamPlatform.getTeamRepositoryService()
.getUnmanagedRepository(REPOSITORY_URI);
assertFalse(repository.loggedIn());
repository.registerLoginHandler(new ITeamRepository.ILoginHandler() {
public ILoginInfo challenge(ITeamRepository repo) {
return new ILoginInfo() {
public String getUserId() {
return "ADMIN";
}

public String getPassword() {
return "ADMIN";
}
};
}
});
repository.login(null);
assertTrue(repository.loggedIn());
return repository;
}
}

Running the test

To simplify matters, we’ll run in local mode (i.e. client and server will be in the same process).  Running this way avoids the need to deploy your common and service plug-ins, and is thus a convenient way to test changes to your common and service plug-ins; if you’re only changing your client plug-in, you’ll want to run your service in a separate Jazz server so that it isn’t necessary to restart the server with each change.

Now we’re ready to run the test we’ve written.  Create a JUnit Plug-in Launch, with TestHelloJazz as the test class, with [No Application] – Headless Mode as the application to run, and with these VM arguments:

  • -Dcom.ibm.team.core.tests.repo=local
  • -Dcom.ibm.team.repository.db.jdbc.location="${target_home}/../../server/repositoryDB"

The first argument specifies to use local mode and the second argument identifies the location of the Jazz repository database. This example does not make explicit use of the repository database, but the Jazz Team Server still needs to be able to locate a database. The database path shown above should point to the default one. (Note that the HelloJazz download includes a launch file named TestHelloJazz.launch with the settings described here.)

When we run this, we’ll find that HelloJazzClientLibrary.getService throws an exception because it can’t get an implementation of IHelloJazzService, for the obvious reason that we’ve not yet implemented this on the server.

Service Plug-in

Create the Service Plug-in

Our server-side implementation of the IHelloJazzService will be in plug-in com.example.hellojazz.service. Follow the same steps as before for creating the plug-in.

As this plug-in is used (only) on the server, its dependencies are correspondingly different:

  • com.ibm.team.repository.common
  • com.ibm.team.repository.service
  • com.example.hellojazz.common

Declare the Service Implementation

Let’s now declare the service implementation via the com.ibm.team.repository.service.serviceProvider extension point in the service plug-in’s plugin.xml file:

<extension
point="com.ibm.team.repository.service.serviceProvider">
<serviceProvider
componentId="com.example.hellojazz"
implementationClass=
"com.example.hellojazz.service.internal.HelloJazzService">
<provides>
<providedService interface=
"com.example.hellojazz.common.service.IHelloJazzService"/>
</provides>
</serviceProvider>
</extension>

We used here the same componentId value (com.example.hellojazz) that we used in the common plug-in.

Implement the Service

Service implementations must extend AbstractService, a class in the Jazz Repository component. Other than that detail, our service implementation is very simple, in keeping with our goal of creating a simple Jazz component:

package com.example.hellojazz.service.internal;

import com.example.hellojazz.common.service.IHelloJazzService;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;

public class HelloJazzService
extends AbstractService
implements IHelloJazzService {

public String sayHello() throws TeamRepositoryException {
return "Hello Jazz!";
}
}

Running the Test, Again

When we run the test now, we find that it passes:

TEXT

A good learning exercise at this point is to set breakpoints in the test, and in the client and server implementations, and run the test again under the debugger. This allows one to examine the call stacks that are involved.

You’re ready now to embark on the exciting task of developing your own Jazz component. Have fun!


Bonus Client UI Plug-in

The source code download for this example now contains an additional plug-in, com.example.hellojazz.rcp.ui.  This plug-in demonstrates how to create a simple Eclipse UI for the HelloJazz service.

A launch configuration called HelloJazzClient is been provided with the plug-in.  Open this configuration (Run > Open Run Dialog…) and verify that the VM argument -Dcom.ibm.team.repository.db.jdbc.location correctly identifies your test repository database.  Now run the configuration and wait for the Jazz client to start.  In the new Jazz client, find or open the Team Artifacts view and create a new Jazz Repository Connection with the values shown below  (use ADMIN for the password in the dialog).

The "Create a Repository Connection" dialog.

Now find your new repository connection in the Team Artifacts view, right-click on it and select Call IHelloJazzService from the context menu.

invoking the "Call IHelloJazzService" context-menu action in the Team Artifacts view

You should see a message dialog indicating the service method was called. (Note that the repository URI may differ from what you typed in, as shown below.)

message box showing the results of calling the service method

To understand what happened and why it worked, first examine the plugin.xml in the com.example.hellojazz.rcp.ui plug-in.  It contains an object contribution popup menu action for the object class ITeamRepository.

   <extension
point="org.eclipse.ui.popupMenus">
<objectContribution
id="com.example.hellojazz.rcp.ui.helloJazzPopup"
objectClass="com.ibm.team.repository.client.ITeamRepository">
<action
class="com.example.hellojazz.rcp.ui.actions.HelloJazzAction"
id="com.example.hellojazz.rcp.ui.helloJazzAction"
label="Call IHelloJazzService">
</action>
</objectContribution>
</extension>

The object you see in the Team Artifacts view representing your repository connection is an ITeamRepository, so the action defined here will be added to its context-menu.  Now note the action class HelloJazzAction and examine that class.  You will see code to obtain the IHelloJazzClientLibrary and call the sayHello() method, as was done in the JUnit test case above.  However there is additional code here to obtain the ITeamRepository from the UI selection and to make the service method call in a background thread.
Tue, 03 Jun 2008