Skip to main content

Creating an audio playback plug-in for IBM Lotus Sametime Connect V7.5

Kulvir Singh Bhogal (kbhogal@us.ibm.com), Consultant, Software Services for WebSphere, IBM
Kulvir Singh Bhogal works as an IBM Software Services for WebSphere consultant, devising and implementing J2EE-centric solutions at customer sites across the nation. You can reach Kulvir at kbhogal@us.ibm.com.
Mark Talbot (talbotm@us.ibm.com), Developer, Industry Solutions, IBM
Mark Talbot works for IBM as a developer for Industry Solutions. You can reach Mark at talbotm@us.ibm.com.

Summary:  Learn how to create a plug-in that plays back an assigned audio file when an instant message is received from a specified Sametime partner.

Date:  24 Oct 2006
Level:  Intermediate
Activity:  1951 views

The Eclipse-based architecture of IBM Lotus Sametime Connect V7.5 allows you to enhance your client to your heart’s content. In this article, you learn how to customize the Lotus Sametime client to play back a user-assigned audio file when an instant message is received from a specific Sametime partner. The end goal of this article is to create the WAV Playback plug-in. During your study, you learn what programmatically happens when a Sametime instant message is received by the Lotus Sametime client. You also see how to create a custom reaction to an instant message being received. Accordingly, after reading this article, you can embark on your own escapades of Sametime client plug-in development with your newfound knowledge.

This article is intended for application developers interested in customizing the Lotus Sametime Connect client. Some familiarity with Eclipse is helpful, but not required. Knowledge of Java is expected.

A preview of the WAV Playback plug-in

The WAV Playback plug-in for Lotus Sametime Connect V7.5 lets you customize the client by assigning an audio file to a select Sametime partner. You manipulate the Sametime user interface to add the "Assign Audio file for IMs" menu item to the context menu that appears when you right-click a partner (see figure 1).


Figure 1. "Assign Audio File for IMs" menu item
'Assign Audio File for IMs' menu item

After selecting the "Assign Audio File for IMs" context menu item, you are presented with a window like the one shown in figure 2 from which you can assign an audio file that is played when IMs from the selected Sametime partner are received. The plug-in keeps track of the mappings of Sametime partners to sound files in a separate properties file.


Figure 2. Selecting an audio file
Selecting an audio file

Obtaining and preparing the Eclipse 3.2 IDE

We used the Eclipse 3.2 IDE to develop the WAV Playback plug-in, and we assume that you have the Eclipse 3.2 IDE installed. If not, go to the Eclipse Web site [http://www.eclipse.org/downloads/] to acquire the software.

We also assume that you are familiar with the Eclipse IDE for development. If not, we suggest you familiarize yourself by checking out some of the resources we have listed in the Resources section before moving forward.

Changing the Eclipse target platform

Changing the target platform from the Eclipse platform to the Sametime platform allows you to extend the Sametime platform as opposed to extending the Eclipse platform.

To change the target platform to Lotus Sametime V7.5 in Eclipse, follow these steps:

  1. Choose Window - Preferences to open the Preferences dialog box.
  2. In the left-hand pane, expand the Plug-in Development item, and then select Target Platform.
  3. In the right-hand pane, the Target Platform Preference Page appears. Click Browse to change the location.
  4. Change your target platform to the location of the directory representing the Lotus Sametime V7.5 plug-ins directory on your system. If you installed Lotus Sametime V7.5 in the default location, the target platform location is C:\Program Files\IBM\Sametime Connect 7.5\sametime.
  5. Click Reload. You now see the Sametime plug-ins as shown in figure 3.
  6. Click OK to return to the Plug-in Development perspective.

    Figure 3. Target Platform
    Target Platform

Creating a plug-in project

To extend the Sametime UI with your audio file playback functionality, you need to create a plug-in. But before you create the Sametime plug-in, you need a plug-in project to house the plug-in during your development phase. To create a plug-in project, follow these steps:

  1. In Eclipse, choose File - New - Project.
  2. From the New Project wizard, select Plug-in Development - Plug-in Project. Then click Next.
  3. Specify com.devworks.example.sound as the project name and click Next.
  4. In the Plug-in Content panel (as shown in figure 4), specify WAV Playback for the plug-in name. For the plug-in provider, specify DeveloperWorks.
  5. Notice that the option "This plug-in will make contributions to the UI" is not selected in figure 4. The Sametime APIs provide a means of extending the Sametime UI. Because you are extending only the Sametime UI, making a contribution to the Eclipse UI is not necessary.


    Figure 4. New Plug-in Project wizard
    New Plug-in Project wizard

  6. Accept the remaining default values and click Finish.

Setup plug-in dependencies

To allow the Sametime plug-in to resolve the Sametime graphical interface and messaging classes needed for development of the plug-in, edit the plug-in's dependencies. To do this, follow these steps:

  1. In Eclipse, open your MANIFEST.MF file inside the META-INF folder.
  2. Next, select the Dependencies tab, which opens the Dependencies view.
  3. In the Required Plug-ins area, click Add. The Plug-in Selection dialog box appears as shown in figure 5.
  4. Select the com.ibm.collaboration.realtime.people plug-in and click OK.


    Figure 5. Plug-in Selection dialog box
    Plug-in Selection dialog box

  5. Repeat steps 3 and 4 for the com.ibm.collaboration.realtime.messages, com.ibm.collaboration.realtime.core, and com.ibm.collaboration.realtime.magiccarpet plug-ins.
  6. Save the Dependencies page by pressing Ctrl + S. The Dependencies page should look like figure 6.


    Figure 6. Dependencies page
    Dependencies page


The AudioPlayer class and its playBack method

The task of playing audio files is carried out by the com.devworks.example.customsound.AudioPlayer class. Audio playback is made quite simple courtesy of the innate media facilities of the Java language. Below is the source code of the AudioPlayer class.

public class AudioPlayer 
{
  public static void playBack(String filename) throws MalformedURLException 
 {
    File file = new File(filename);
    URL url = file.toURL();
    java.applet.AudioClip clip = java.applet.Applet.newAudioClip(url);
    clip.play();
  }
}

The actual work of playing back the audio file is carried out when you load your file as a java.applet.AudioClip (oddly enough, you can use the code even though you are not using a Java applet). Next, all you have to do to play your AudioClip object is to use the play method.

NOTE: The AudioPlayer class can play the following audio file formats: WAV, AIF, MID, AU, and RMF.


The sound.properties file

The file association of the sound file to be played by the AudioPlayer class when an instant message is received for a given Sametime partner is housed in a properties file called sound.properties. This properties file must reside in the top level directory of the plug-in. You can edit the properties file manually. The format of the file is simply the Sametime partner ID followed by an equals sign, which in turn is followed by the location of the sound file. Below is an example of the contents of the sound.properties file:

talbotm@us.ibm.com=C\:\\lotus\\flg\\media\\cheering.wav
kbhogal@us.ibm.com=C\:\\lotus\\flg\\media\\carskid.wav


Notice the need to use double slashes in the file paths. To facilitate a more intuitive user experience, we allow the Sametime client user to update the association of Sametime partners to the location of the sound files graphically through an addition to the context menu that appears when a client user right-clicks a Sametime partner as depicted in figure 1. We cover how to add the "Assign WAV file for IMs" option to the context menu in the section "The PopupMenus extension point."

The SoundStore class

The sound.properties file is accessed through the com.devworks.example.sound.SoundStore class. The SoundStore class has a getter and setter method for getting and setting the location of a sound file for a particular Sametime partner. The following code snippet shows a section of the SoundStore class.

The Properties object is used to house the contents of the sound.properties file. The boolean variable initialized acts as a toggle variable for indication of whether or not the SoundStore object has been initialized.

public class SoundStore {
	public static Properties properties = new Properties();

	public static boolean initialized = false;

	private SoundStore() {
	}

Shown in the following code snippet, the initSoundCache method reads the contents of the sound.properties file, which as explained earlier, stores the name-to-value mappings of Sametime partner ID to sound file locations.

	private static void initSoundCache() {
		properties = new Properties();
		Activator soundActivator = Activator.getDefault();
		Bundle soundBundle = soundActivator.getBundle();
		URL propFile = soundBundle.getResource("sound.properties");
		try {
			if (propFile != null) {
				InputStream inStream = propFile.openStream();
				properties.load(inStream);
				inStream.close();
				initialized = true;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

In the previous code, the URL location of the sound.properties file is discovered through the shared bundle instance. With the URL location, you can open a stream from which to read the properties file. The property file data is then read into a Properties object using the InputStream. After the properties are loaded, the InputStream is closed. Finally, the initialized variable is set to true, indicating that the properties file has been read and the properties object populated.

In the following code snippet, the getSound method reads the Properties object to discover the sound file location assigned to a given Sametime partner ID. The method returns the file location if one was assigned. If there is no sound file assigned for a given partner, the method returns an empty string.

	public static String getSound(String partnerId) {
		String partnerSound;
		if (!initialized)
			initSoundCache();
		Object soundObject = properties.get(partnerId);
		if (soundObject != null)
			partnerSound = (String) soundObject;
		else
			partnerSound = "";
		return partnerSound;
	}

At the beginning of the setSound method, we check to see if the SoundStore object is initialized (as indicated by the value of the initialized boolean variable). If the SoundStore object is not initialized, then we initialize it with the initSoundCache method described previously. Next, the get method of the Properties object is used to retrieve the sound file name for a given partner.

The setSound method of the SoundStore class is responsible for updating the Properties object and the sound.properties file with a given Sametime partner ID and a file name.

	public static boolean setSound(String partnerId, String soundFile) {
		boolean success = false;
		if (!init)
			initSoundCache();
		properties.setProperty(partnerId, soundFile);
		URL propURL = Activator.getDefault().getBundle().getResource(
				"sound.properties");
		try {
			if (propURL==null)
				propURL = new URL("file:\\\\\\sound.properties");
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}
		String bundleLocation = Activator.getDefault().getBundle()
				.getLocation();
		File propFile = new File(bundleLocation + propURL.getPath());
		FileOutputStream out;
		try {
			out = new FileOutputStream(propFile);
			String header = null;
			properties.setProperty(partnerId, soundFile);
			properties.store(out, header);
			out.close();
			success = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return success;
	}

Similar to the getSound method, the setSound method first checks to see if the SoundStore class has been initialized. If not, an initialization call is made from an invocation of the initSoundCache method. The URL of the sound.properties file is obtained from the getResource method of the shared bundle instance. As a fail safe, if the sound.properties file is not created, one is created. With that URL of the file, a FileOutputStream object is created. Finally, the sound.properties file is updated by a FileOutputStream object with updated Sametime partner to audio file association data.

Later, you will see how the SoundStore object is used by the plug-in. The graphical component of our plug-in uses the SoundStore object as described in the next section.


The PopupMenus extension point

To add the "Assign Audio File for IMs" context menu item that appears when right-clicking a Sametime partner as shown in figure 1, create a class that extends the LiveNameActionDelegate class. For more information about this topic, we suggest you read the developerWorks Lotus article, "Extending the Lotus Sametime client with an LDAP directory lookup plug-in," which addresses this concept in depth. We name our class: AssociateSound.

When the menu item "Assign Audio file for IMs" is selected, the run method of the AssociateSound class shown below is triggered.

public class AssociateSound extends LiveNameActionDelegate {
	public void run(IAction arg0) {
        Person persons[] = getSelectedPersons();
		Person person = persons[0];
		if (person != null) {
			String id = person.getContactId();
			System.out.println("Associating Sound for " 
			+ id); //$NON-NLS-1$
			setUserSoundBox(id);
	}
}

In the run method, the Person object’s getSelectedPersons method is used to get the ID of the Sametime partner who was right-clicked. A call is then made to the setUserSoundbox() method:

	public static void setUserSoundbox(String id) {
		Shell shell = new Shell();
		FileDialog dialog = new FileDialog(shell);
		String file = SoundStore.getSound(id);
		dialog.setFileName(file);
		dialog.setFilterExtensions(new String[] 
			{"*.wav; *.aif; *.mid; *.au; *.rmf"});
		dialog.setFilterNames(new String[] 
			{"Supported Audio Files (*.wav; *.aif; 
			*.mid; *.au; *.rmf)"});
		dialog.open();
		String fileName = dialog.getFilterPath() 
			+ File.separator  + dialog.getFileName();
		SoundStore.setSound(id, fileName);
	}
}

The setUserSoundbox method displays a FileDialog widget as shown in figure 2, prompting the user to select the location of an audio file. After the user selects the location of the audio file, the location of the audio file is associated with the Sametime partner’s ID in the properties file (using the SoundStore object’s setSound method).

Before displaying the FileDialog widget, you first obtain the location of the existing audio file associated with the Sametime partner (if an audio file association has already occurred) through the SoundStore.getSound(id) method. The default location of the FileDialog is set to the location of the current sound file set for the Sametime partner through the dialog.setFileName(file) method. You then filter the file extensions to show only sound files supported by the AudioPlayer object with the dialog.setFilterExtensions(new String[]) method. After a sound file is designated by the Sametime client user, update the SoundStore object with the user-specified location of the sound file.

The org.eclipse.ui.popupMenus extension point is responsible for associating the LiveNameActionDelegate class (that is com.devworks.example.sound.AssociateSound) with a context menu item that appears when you right-click a Sametime partner ID. You define the class com.devworks.example.sound.AssociateSound as the class that handles the "Assign Audio file for IMs" menu item in the extension point definition below, which must be added to your plugin.xml file.

<extension point="org.eclipse.ui.popupMenus">
	<objectContribution
		adaptable="false"
		id="com.devworks.example.sound.prersonselection"
		objectClass=
	"com.ibm.collaboration.realtime.livenames.PersonSelection">
		<action
			class="com.devworks.example.sound.AssociateSound"
			enablesFor="1"
			  id="com.devworks.example.sound"
			label="Assign Audio File For IMs"
			style="push"/>       
	</objectContribution>
</extension>

We cover the org.eclipse.ui.popupMenus extension point with much more depth in the developerWorks Lotus article, "Extending the Lotus Sametime client with an LDAP directory lookup plug-in." We suggest you take a look at that article for more in-depth coverage of the concept.


Event notification

When a Sametime client receives an instant message, programmatically it receives an IMTextReceived object. Lotus Sametime Connect V7.5 provides a messaging bus that allows components to communicate with one another. Sametime components that partake in messaging are known as participants. To receive messages from another component, the participant only needs to know how to interact with the messaging bus.

Before we dive into the implementation specifics of playing a sound file when an instant message is received, we first give a high-level overview of how to access the messaging bus:

  1. To access the messaging bus, first create a subclass of DefaultMessageHandler, a class that handles messaging events.
  2. Next, create a subclass of MessageHandlerAdapter, the class that plugs into the messaging bus through an extension point.
  3. Then override the default constructor of the MessageHandlerAdapter class to call its one argument constructor with the DefaultMessageHandler (which was created previously) as a parameter. This associates your DefaultMessageHandler class with your MessageHandlerAdapter class.
  4. Next register your MessageHandlerAdapter class with the com.ibm.collaboration.realtime.messages.MessageHandlerListener extension point. Registering with the extension point ties your MessageHandlerAdapter class into the Sametime messaging bus.

In the subsequent sections, we discuss our DefaultMessageHandler class and MessageHandlerAdapter class.

Introducing our DefaultMessageHandler class: SoundHandler

To create a customized reaction to the receipt of an instant message, it is necessary to create a class that extends the com.ibm.collaboration.realtime.messages.DefaultMessageHandler class. The class you create needs to override the handleMessage method it inherits from its parent class. In our case, we created a class named com.devewokrs.example.sound.SoundHandler:

public class SoundHandler extends DefaultMessageHandler {	
	public void handleMessage(ImTextReceivedMessage message) {
		String soundFile = "";
			String partnerId = message.getPartnerID();
			soundFile = SoundStore.getSound(partnerId);
			if ((soundFile != null) && 
			(soundFile.trim().equals(""))) {
				File soundlocation = new File(soundFile);
				if (soundlocation.exists()) {
					try {
						AudioPlayer.playBack(soundFile);
					} catch (MalformedURLException e) {
						MessageBox mBox = 
						new MessageBox(new Shell());
						mBox.setMessage("Invalid Sound File");
						mBox.setText(e.getMessage());
						mBox.open();
					}
				}
			}
	}

The DefaultMessagerHandler class has an associated method to handle the receipt of every type of message that subclasses the Message class. The WAV Playback plug-in handles only text messages. Accordingly, in the code above, we overrode the handleMessage method, which takes the ImTextReceivedMessage class as a parameter. This method is invoked when the Sametime client receives a text message from a partner.

In the previous code, you first get the ID of the partner who sent the instant message. Then look up the location of the sound file for that partner through a call to the SoundStore class covered earlier. After you discover the location of the sound file, verify that the sound file exists, and then play the sound with the AudioPlayer class. If there is an error in playing back the file, a MalformedURLException is displayed in a message box.

As mentioned earlier, the DefaultMessageHandler class has a method for handling every Sametime class that subclasses the Message class. Unless these message handler methods are explicitly overridden by your subclass of the DefaultMessageHandler, the method inherited from the DefaultMessageHandler parent class performs a no-op. For example, there are methods for receiving an ImTextReceivedMessage that we are overriding. Similarly, there is a corresponding method com.ibm.collaboration.realtime.messages.FileReceivedMessage object (for when a file is received) and so forth.

Though we do not do this, you have the option to subclass the Message class. To handle these custom Message objects, there is not an innate method in the DefaultMessageHandler class. Rather, these Message subclass objects are handled by the handleDefaultMessage abstract method, which you must include in a class that extends the DefaultMessageHandler class. Because you are not creating a subclass of the Message class, the handleDefaultMessage method does nothing as shown in the following code:

	public void handleDefaultMessage(Message arg0) {
	}
}

Tying the MessageHandler class into the messaging bus: MessageHandlerAdapter

You need a way for your SoundHandler class to be notified when an ImTextReceivedMessage message object is received. This is where the com.ibm.collaboration.realtime.messages.MessageHandlerAdapter class comes into play. This adapter class is the bridge between the MessageHandlerListener extension point (which we cover in the next section) and our DefaultMessageHandler class (or more specifically the SoundHandler class). To establish this bridge, you create the class com.devworks.example.sound.SoundHandlerAdapter, which extends the MessageHandlerAdapter class:

public class SoundHandlerAdapter extends MessageHandlerAdapter {
	public SoundHandlerAdapter(MessageHandler arg0) {
		super(arg0);
	}
	public SoundHandlerAdapter() {
		super(new SoundHandler());
	}
}

Notice in the previous source that we overrode the default constructor to take our DefaultMessageHandler, SoundHandler, as a parameter. Next, you register the SoundHandlerAdapter with the com.ibm.collaboration.realtime.messages.MessageHandlerListener extension point.

The MessageHandlerListener extension point

The final step in registering the SoundHandler class with the Sametime messaging bus is registering the MessageHandlerAdapter class you just defined with the MessageHandlerListener extension point as shown in the following code snippet. The messageHandler extension definition must be added to your plugin.xml file.

<extension point="com.ibm.collaboration.realtime.messages.MessageHandlerListener">
<messageHandler class="com.devworks.example.sound.SoundHandlerAdapter" />
</extension>

In the definition above, you define an extension to the MessageHandlerListener extension point. For the messageHandler tag, specify your MessageHandlerAdapter class namely: com.devworks.example.sound.SoundHandlerAdapter. After a messaging event is received by the Sametime client, the SoundHandlerAdapter is notified. This adapter class in turn notifies the SoundHandler class. If the messaging event is an ImTextReceivedMessage, the handleMessage method of the SoundHandler is triggered. Consequently, if a sound file has been registered for the partner who sent the text message, the sound file is played.


Conclusion

If you are the type of person who likes to customize everything (your car, your pizza, and so on), then the customizable, extensible nature of Lotus Sametime Connect V7.5 by virtue of its Eclipse-based architecture will fit your fancy. In this article, we showed how you can customize the way the Sametime client reacts when an instant message is received. More specifically, we showed you how to programmatically change the Sametime client to play back an audio file (assignable per Sametime partner) when an instant message is received. Of course, what you do when the message is received is up to you! You can "teach" the Sametime client to do other things. You are only limited by your imagination!


Resources

Learn

Get products and technologies

Discuss

About the authors

Kulvir Singh Bhogal works as an IBM Software Services for WebSphere consultant, devising and implementing J2EE-centric solutions at customer sites across the nation. You can reach Kulvir at kbhogal@us.ibm.com.

Mark Talbot works for IBM as a developer for Industry Solutions. You can reach Mark at talbotm@us.ibm.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Lotus
ArticleID=169753
ArticleTitle=Creating an audio playback plug-in for IBM Lotus Sametime Connect V7.5
publish-date=10242006
author1-email=kbhogal@us.ibm.com
author1-email-cc=
author2-email=talbotm@us.ibm.com
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers