This article teaches you how to extend IBM Lotus Sametime Connect so that users can more easily reach out to the coworkers of a chat partner for help. The solution leverages the department hierarchy available in the Profiles component of IBM Lotus Connections. The same solution could easily be extended to help connect your employees to others using relationships unique to your business (for example, those who share particular interests, recognized experts, support organization members, and more).
The standard Lotus Sametime Connect chat window interface includes an Invite Others button as shown in figure 1.
Figure 1. The Invite Others button in Lotus Sametime Connect
Clicking this button opens a dialog box in which you can start an n-way chat as shown in figure 2.
Figure 2. The Invite Others dialog box
Using Eclipse and IBM Lotus Sametime Software Development Kit (SDK), you add the specialized button to the chat window toolbar as highlighted in figure 3.
Figure 3. The chat window's modified toolbar
The Invite Coworkers button presents the same dialog box as the Invite Others button, but it is initialized with the online coworkers of your chat partner.
To understand the coding techniques presented in this article, you should have the following skills and expertise:
- Some Java programming skills
- Familiarity with REST APIs
- Familiarity with the Eclipse IDE
- Awareness of Abdera and XPath, which is helpful, but not required
To run the solution, you must have Lotus Sametime Connect V7.5.1 or V8.0 installed on your workstation, and you must have Web access to an IBM Lotus Connections server. You can confirm that you have access to your company's Lotus Connections server by visiting its Profiles home page (for example, http://<connections.insertyourwebsite>.com/profiles/; contact your system administrator if you don’t know the proper URL).
You can download the example code for this article from the Download section. Two plug-in projects are included:
- A fully coded solution to this article named com.ibm.collaboration.realtime.sample.invitecoworkers
- A partially coded solution to this article named com.lotuslabs.invitecoworkers
Working through this article, you add the necessary code to complete this solution. To set up your development environment, follow the steps outlined below.
NOTE: The solutions were coded using specific versions of the required tools indicated below. More recent versions may work with minor modifications, but this article was tested using the versions below.
- Install Eclipse version 3.2.2.
- Start Eclipse, and then install IBM Lotus Expeditor Toolkit V6.1.1 as described in the section entitled "Lotus Sametime V7.5.1 developer" of the developerWorks article, "Using IBM Lotus Expeditor Toolkit V6.1.1 with IBM Lotus Notes V8 and IBM Lotus Sametime V7.5.1." Confirm that you can launch Lotus Sametime Connect from the Eclipse plug-in development.
- Import the article projects (see the Download section) by choosing File - Import - General - Existing Projects into Workspace. Compiler errors are expected at this time because the solution uses Abdera libraries that are installed in the next step.
- Download Abdera 0.3.0. Extract the abdera.0.3.0-incubating.jdk142.zip file to the article projects as shown in figure 4 (for example, extract the ZIP file to C:\eclipse\workspace\com.lotuslabs.invitecoworkers and C:\eclipse\workspace\com.ibm.collaboration.realtime.sample.invitecoworkers).
- Select the two projects, and then Refresh from the Package Explorer pop-up menu; Eclipse recognizes the new libraries and rebuilds the two projects. There should be no errors shown in the Problems pane after this last step is complete.
- To follow along with the article steps, you modify the com.lotuslabs.invitecoworkers project. The solution project com.ibm.collaboration.realtime.sample.invitecoworkers is for your reference and is not needed at this time; close it before you continue.
Figure 4. Solution projects shown in Package Explorer with Abdera installed
Two technologies are involved in the solution. Lotus Sametime Connect provides the instant messaging user interface, directory services, and chat APIs to the client. The Lotus Connections Profiles server provides department information. By combining these APIs, your extension delivers a richer collaborative experience.
These are the skills you learn:
- How to add new UI elements to Lotus Sametime Connect, specifically the new Invite Coworkers button
- How to access additional information about chat partners, such as email addresses
- How to leverage Lotus Connections Profiles to retrieve information about departmental relationships (manager, coworker, and so on)
- How to map departmental contact information from Lotus Connections Profiles into live Lotus Sametime persons in your Contacts list
Your Eclipse plug-in development environment (PDE) should already be set up as described in the "Prerequisites" section of this article. Some parts of the solution that aren't particularly interesting are already implemented, so you can focus on the skills listed here. As part of this exercise, you use the Eclipse integrated development environment (IDE). The steps in this article assume that you have some experience with the Eclipse IDE.
If you're unfamiliar with the Eclipse plug-in model and the Lotus Sametime Connect application framework, read the capsule summary that follows. It may help you appreciate the points made in this article.
Overview of the Lotus Sametime Connect architecture
One of the unique features of Lotus Sametime Connect is its extensibility. This enables you to customize it with new features that integrate seamlessly into the user interface thanks to the Eclipse plug-in model.
The Eclipse Rich Client Platform (RCP) and IBM Lotus Expeditor serve as the foundation of Lotus Sametime Connect. A key benefit of this choice of platform is that it can integrate seamlessly with other application components, namely other plug-ins. The contributions of these plug-ins can be as simple as a new toolbar button or as complex as a wholly integrated mini-application. The overarching goal of the Lotus Sametime Connect - Eclipse - IBM Expeditor architecture is to provide three benefits: extensibility, integration, and reuse.
The benefit of using Eclipse/Lotus Expeditor as an integration platform is its ability to add new functionality to the environment. A flexible, yet consistent user experience is a strong selling point, but the advantage for Eclipse programmers is the leveraging of their existing skills to develop standard Eclipse RCP line-of-business applications.
Most of the principles of Eclipse application development are directly applicable to creating extensions to Lotus Sametime Connect and other Lotus Expeditor-based products, such as IBM Lotus Notes V8. Even programmers without previous Eclipse application development experience recognize the common object-oriented programming patterns in these principles. For example, Eclipse follows the classic separation of model (Workbench) and view (ViewParts and their lower-level graphical components in the JFace framework). Thus, the clear separation of component responsibility reduces the need to learn about each individual piece to be productive. The Lotus Sametime architecture embraces this same principle.
Figure 5 shows major Lotus Sametime Connect components. The Java runtime, Eclipse RCP, and Lotus Expeditor layers form the foundation common to IBM's managed client products, which include Lotus Notes V8. The components enclosed by the box are specific to Lotus Sametime Connect and include public and implementation-specific plug-ins.
Figure 5. Lotus Sametime Connect components
Because Lotus Sametime is based on the Eclipse platform, the application is essentially a number of plug-ins tied together. You can think of each plug-in as a package of Java classes that defines a specific functionality. Some of these packages are designed to be extended by others, allowing the development of additional plug-ins that enhance the base functionality. You have to determine the base functionality your plug-in needs to declare the appropriate plug-ins as dependencies. This provides access to their library of classes and the extension points as a foundation on which you can build.
Given the extension points provided by your plug-in's list of required plug-ins, you next decide where your plug-in integrates with the user interface. Each plug-in can have multiple extension points based on the functions they control. For instance, dependency upon Lotus Sametime's Chat Window plug-in (referred to by its plug-in identifier com.ibm.collaboration.realtime.chatwindow) provides access to an extension point for adding to the chat window toolbar and to another for creating a pop-up add-on (a pane that appears below the chat window Send area). The proper extension point has to be identified, and an extension must be defined. Eclipse makes this simple with template-driven wizards that provide step-by-step instructions.
After the extension points are identified and your plug-in's extensions defined, you code the Java classes needed to perform the new functionality. Eclipse provides assistance by recommending the necessary superclasses and/or interfaces needed for some extensions.
For the purpose of this article, we work with only one extension point for adding to the user interface and a few components to implement the desired function, presenting the list of coworkers of our chat partner in the Invite Others dialog box. The components that we cover include:
- User interface components to contribute a button to the chat window
- Services to access implementations of the directory services and other features
- Directory to map emails to live Lotus Sametime person objects
- Messaging services to initiate a chat session
You also learn how to retrieve data from the Lotus Connections Profiles server using REST APIs.
Add the Invite Coworkers button to the chat window
The plug-ins that make up Lotus Sametime components declare extension points where developers can access established classes and interfaces for use in their own development. Extension points are the means by which you can extend the functionality of Lotus Sametime. You can define your own extension points in the plug-ins you create to allow other developers to build on functionality that you provide; however, that feature is not covered in this article.
Eclipse's application user interface, referred to as the Rich Client Platform, defines graphical widgets (entry fields, buttons, toolbars, and so on) and higher-level user interface elements (main window or Workbench, views, editors, and more). Lotus Sametime Connect supports and builds upon the Eclipse user interface extensions. For example, your plug-in could contribute menus to the Lotus Sametime Connect contact list main window using the Eclipse extension point, org.eclipse.ui.actionSet. Other extension points are defined by Lotus Sametime Connect. Such extensions are declared to the plug-in model runtime in the same manner as those provided with the base Rich Client Platform, but they have parameters and values specific to Lotus Sametime as in this article's solution.
- If you haven't done so already, open the Eclipse IDE's shortcut on the desktop. This shortcut opens to the skeletal plug-in project com.lotuslabs.invitecoworkers that you complete. Most of the missing pieces are snippets of Java code that this article covers shortly. Your first task is to create a toolbar button extension to the chat window toolbar shown earlier.
- Begin by opening the plug-in manifest editor by double-clicking the plugin.xml file in the com.lotuslabs.invitecoworkers project. This opens a wizard-like editor with multiple tabbed pages. One page shows the source code (labeled plugin.xml); the rest of the pages contain editors that can prompt for the required fields and modify the manifest file source accordingly. Turn to the Extensions page, and then click Add to create a new extension as shown in figure 6.
Figure 6. The Extensions dialog box
This dialog box presents all the many extension possibilities available to you. You can quickly narrow down the choices by entering *chatwindow* in the entry field, and then selecting com.ibm.collaboration.realtime.chatwindow.chatAction from the list. Selecting the extension updates a brief description of its purpose in the information box below it.
- Click the Finish button to add the extension to your plug-in.
- Define a chat action to handle the button's behavior. The extension created in the prior step can define more than one chat action. You define a chat action by selecting the extension, and then choosing the New - chatAction pop-up menu choice.
The pane to the right of the extensions updates to match the current selection. There you can finish parameterizing the extension. Update the values to match those shown in figure 7 and listed here:
- In the id field, enter com.lotuslabs.invitecoworkers.chatAction.
- In the class field, click the Browse button and select com.lotuslabs.invitecoworkers.InviteCoworkersToChatAction.
- In the type field, select buddy from the drop-down list.
- In the tooltipText field, enter STIG: Invite Coworkers.
- In the image field, click Browse and select images/invitecoworkers.png.
- In the path field, enter buddy/end.
- In the showsFor field, select both from the drop-down list.
Figure 7. The Extension Element Details dialog box
The behavior of the newly contributed toolbar button is defined by the class that responds to user's requests, InviteCoworkersToChatAction. A skeletal definition is provided, so there's no need to create one from scratch. Fill in the class parameter as shown in figure 7.
- You've created the extension. Confirm that it is correct by turning to the plugin.xml page to see the source. Does it match the excerpt shown in listing 1?
Listing 1. chatAction extension<extension point="com.ibm.collaboration.realtime.chatwindow.chatAction"> <chatAction class="com.lotuslabs.invitecoworkers.InviteCoworkersToChatAction" id="com.lotuslabs.invitecoworkers.chatAction" image="images/invitecoworkers.png" path="buddy/end" showsFor="both" tooltipText="STIG: Invite Coworkers..." type="buddy"/> </extension>
The key parameters are these:
- class. An instance of this class is created when the user first selects the menu choice and its run(...) method is invoked.
- image. Graphic that represents the toolbar button.
- path. The location where in the toolbar the new contribution should be inserted. There are a number of predefined locations grouped by similar functionality. In this example, the action is added to the partner-related choices in the toolbar at the end (buddy/end).
- type. The location where the contribution should be placed (format for the toolbar in the Send area, list for the toolbar in the list of participants in the n-way chat area, and buddy for the main toolbar).
- Save your changes. Although your contributed action does not do anything when selected, let's quickly confirm that it is shown in Lotus Sametime Connect by launching it from the IDE. Choose Run - Run to display the launch configuration dialog box.
One of the most powerful features of the Eclipse development environment is its support for a rapid code-test-correct cycle. The launch configuration allows you to define a test environment based on an already installed Eclipse product. In this case, Lotus Sametime Connect is already installed and the Eclipse IDE refers to it. Your project com.lotuslabs.inviteothers is merged with this installation by the PDE's runtime launch configuration so that you can test your plug-in without needing to deploy or otherwise modify your product installation. Any local changes you make to the Lotus Sametime Connect environment launched from the IDE (for example, preferences, previous login, or window location) are stored in a separate directory managed by the Eclipse plug-in development environment (PDE) tools.
- Select ST8.0 under Client Services, and then click the Run button as shown in figure 8.
Figure 8. Runtime configuration
- After a brief delay, Lotus Sametime Connect opens with the login dialog box. Log in and confirm that your newly contributed action is present in the chat window toolbar by starting a chat (opening a chat with yourself works). Clicking the Invite Coworkers button displays an informational message that you replace with the real code later.
Retrieve coworker contact information from Profiles
Lotus Connections is social software that helps your employees leverage information and relationships. It includes these features:
- Communities. Communities allow groups of people who share a common interest to collaborate with one another by exchanging and sharing information or interacting with one another using IBM Lotus Sametime and email.
- Activities. Activities are designed to help you organize the information and the people you need so that you can effectively execute on assignments. For example, Activities help you track your work with a dashboard, share tasks with others, and manage individual and team to-dos.
- Blogs. Blogs allow you to present your own ideas and learn from others.
- Dogear. Dogear allows you to save and share bookmarks.
In addition, Lotus Connections includes a feature you can combine with Lotus Sametime Connect:
- Profiles. A directory that typically includes a person's photo, reporting structure, name, name pronunciation, the time zone the person works in, and information about the individual's expertise and areas of interest.
Coworker profiles – what data is available?
The Lotus Sametime directory service is intentionally lightweight. It doesn't include, for example, a person's street mailing address or phone number. The directory does include the person's email address, preferred name, and login (which is frequently the same as the Lotus Sametime login ID). Lotus Connections Profiles, in contrast, contains quite a bit of information about those listed in its directory:
- Mailing address
- Phone number
- Department members
- Manager/report-to chain
- Profile/background information
- Personalized tags (related to Dogear)
This article combines these two information sources (Lotus Sametime directory, Lotus Connections Profiles), taking advantage of the Lotus Connections Profiles API. Before we get into the details, spend a moment familiarizing yourself with the Lotus Connections Profiles user interface as shown in figure 9; it's accessible from your company's Lotus Connections URL, for example, http://<connections.insertyourwebsite>.com/profiles/.
Figure 9. Lotus Connections Profiles user interface
Try searching for some of your coworkers using wildcard searches. The searches you invoke from the Web user interface are the same as the searches your plug-in extension does to find the coworkers of your chat partner.
Coworker profiles – format of returned data = Atom
The Lotus Connections Profiles data presented as an HTML page can also be queried using the REST APIs, which return Atom feed format. For example, the search that follows would return a user-friendly HTML page for John Smith:
http://<connections.insertyourwebsite>.com/profiles//html/profileView.do?uid=JSmith
The HTML page's data can be retrieved in a more program-friendly format. To see the distinction between the HTML page source and the Atom feed, enter the URL that follows, and then view the Atom format source data (right-click and choose View Page Source; you may need to choose View - Wrap Long Lines from the source window):
http://<connections.insertyourwebsite>.com/profiles//atom/profile.do?uid=JSmith
Other Lotus Connections Profiles REST API examples are shown on this page:
http://<connections.insertyourwebsite>.com/profiles//atom/apihelp.do
You may recognize the results shown in your browser as Atom feed format. Most browsers recognize these formats and automatically offer to subscribe to them. The format of an example query is shown next. The volume of output may seem imposing, but your Lotus Sametime Connect extension easily parses these feed results to find the coworkers for the current chat partner by taking advantage of two packages, Abdera ("Atom feed to DOM") and XPath ("select specific nodes of a DOM tree").
Note that the nodes of this XML document are tagged (for example, <a class="email" ...> and <div class="x-office">) for easier parsing. The code takes advantage of these markers to excerpt only the required elements as highlighted in listing 2.
Coworker profiles – get chat partner email
The key with which you search the Profiles database is the chat partner's email address, which is stored in the Lotus Sametime directory and the Lotus Connections Profiles server. The partners involved in a chat are accessible to your InviteCoworkersToChatAction.run method when the user clicks the toolbar button. See listing 3.
Listing 3. Get partner email
DirectoryInfo directoryInfo =
handler.getFirstPartner().getPerson().getDirectoryInfo();
String partnerEmail = (
String) directoryInfo.get(DirectoryInfo.MAIL_ADDRESS);
|
The method's handler parameter is an instance of ChatWindowHandler, a class that acts as your interface to the Lotus Sametime Connect chat window. In addition to the chat partner(s), it keeps track of such things as the display state of the chat window user interface elements (for example, the pop-up add-on, which is the area below the Send Area that displays during a file transfer) and defines methods for adding to the chat transcript.
Enter the code excerpt shown in listing 3 into the InviteCoworkersToChatAction's run method (code snippets are provided in code_snippets.txt of the com.lotuslabs.invitecoworkers project for your convenience). You need to add import statements to account for newly referenced classes. Pressing Ctrl+O (organize imports) does it in one step.
The Chat Window Handler has query and action methods that your extension can use. A chat session typically involves two or more people; the first line of code retrieves the first chat partner and maps that partner to the corresponding live person. The Person instance represents someone known to Lotus Sametime Connect and can return such information as his status (available, away, do not disturb) or his associated directory information.
Call the Lotus Connections Profiles server or not?
Given the chat partner's email address, you may be tempted to simply call the Lotus Connections Profiles server to retrieve her coworkers' email addresses. That would technically work, but keep in mind that your button's run method executes in the user interface thread. If your code makes a potentially long-running call that blocks, the Lotus Sametime Connect user interface hangs. If you've ever seen the Microsoft Windows message "Application XXX is not responding," you know what that means.
Fortunately, Eclipse has a helpful framework to address the need for background tasks called jobs. A job instance represents a thread of execution from a pool of background threads. Creating one is as easy as subclassing the job class, overriding the default run method, and scheduling your job instance for execution. The job manager executes your job's run method at the next opportunity. The project already contains the skeletal CoworkerLookupEmailsJob class. Enter the following code in InviteCoworkersToChatAction.run to create an instance and run it:
CoworkerLookupEmailsJob job =
new CoworkerLookupEmailsJob(partnerEmail, this);
job.schedule();
The constructor includes the email address the job should use to retrieve coworkers' email addresses. The second parameter, this, is the object that is notified when the job completes using the protocol of notification messages defined by your plug-in's ICoworkerLookupHandler interface shown here in listing 4.
Listing 4. ICoworkerLookupHandler interface
public interface ICoworkerLookupHandler {
public void emailLookupsComplete(String emails[]);
public void contactLookupsComplete
(ContactInfo[] cis, int failedCount);
}
|
We look more closely at InviteCoworkersToChatAction's handling of the email lookup from the Lotus Connections Profile server and the contact lookup from the Lotus Sametime directory later in the article. The only required job method for your CoworkerLookupEmailsJob instance is the run(...) method as shown in listing 5.
Listing 5. CoworkerLookupEmailsJob.run is called from job's thread pool
protected IStatus run(IProgressMonitor pm) {
String emails[] = getCoworkerEmails();
handler.emailLookupsComplete(emails);
return Status.OK_STATUS;
}
|
This method is called from one of the job pool threads as part of the background processing. The next sections cover the heart of this class, the getCoworkerEmails method. As shown previously, this method is called from the job's run method and the handler (InviteCoworkersToChatAction) is notified of its completion before the thread terminates.
Coworker profiles – getting and parsing Atom
Queries to the Lotus Connections Profiles server are returned in Atom feed format. To simplify your coding, use the Apache Abdera framework. It includes a parser capable of handling Atom feeds, returning a top-level document and entry documents for easier processing. Your code doesn't have to worry about parsing XML or validating the format. Another framework called XPath helps navigate the document nodes returned by Abdera. Together it's easy to locate and excerpt a portion of the Atom response. Follow these steps:
- Create Abdera and XPath singletons at the beginning of the CoworkerLookupEmailsJob's getCoworkerEmails method. To save the cost of initializing the Abdera/XPath frameworks, do it once at the beginning and save the instances. The packages are thread safe:
Abdera abdera = new Abdera();
XPath xpath = abdera.getXPath();
Parser parser = abdera.getParser(); - Declare local variables as shown in listing 6.
Listing 6. Local variable declarationsURI uri; InputStream in; Document doc; List emailList; int i;
The URI identifies the source of the Atom feed, namely the Lotus Connections Profiles server. The input stream is the content coming from the server, the document is the root of the Atom feed result, and the emailList is the list of returned coworker email addresses.
- Retrieve and parse the Atom feed results for the reporting chain. An Activator is the singleton class that is created when the plug-in is first loaded. It's a handy place to locate global data such as the user's selected server (for example, the code Activator.getDefault().getServerURI() shown in listing 7). The rest of the URI is the desired service (reportingChain.do) and parameters (email= and format=lite). The Abdera parser reads and parses the return result on demand as shown in listing 7.
Listing 7. Initialize Abdera parseruri = new URI(Activator.getDefault().getServerURI() + "reportingChain.do?email=" + email + "&format=lite"); in = uri.toURL().openStream(); doc = parser.parse(in, Activator.getDefault().getServerURI());
Insert this code next in the getCoworkerEmails method. You may be wondering why the code queries the reporting chain and not the chat partner's coworkers directly. The Lotus Connections Profiles API defines queries for the reporting chain or the managed persons; therefore, to get someone's coworkers, you must query the "managed people" of their manager.
TIP: The code in listing 7 and portions of the code that follow should be enclosed in a try/catch block. For debug purposes, the exception block should print to the system console (e.printStackTrace()). Add catch/try blocks as needed. The Eclipse editor prompts for them where needed with a small light bulb icon to the left of the line in question. Click it to see a list of proposed corrections.
- Locate the desired nodes in the reporting chain. Remember that long listing of XML a few steps back? Helping you process it easily is where XPath comes in. Using a regular expression like syntax, XPath selects only those nodes of interest, specifically those tagged with the class=email element:
emailList = xpath.selectNodes("//*[@class='email']", doc);
in.close(); - Extract the required data from the selected nodes. The only nodes that are returned are those matching the selection criteria. Listing 8 is an example of the node in the XML result.
Listing 8. Email address within Profile result<a class="email" href="mailto:johndoe@us.ibm.com"> johndoe@us.ibm.com </a>
Thus, the node is <a> with attributes class and href plus the data between the <a> and </a>. The code in listing 9 isolates the emails from the href attribute and stores them in the job's return result.
Listing 9. Extract matching email addressesString reportingChainEmails[] = new String[emailList.size()]; i = 0; Iterator iter = emailList.iterator(); while (iter.hasNext()) { ExtensibleElement element = (ExtensibleElement) iter.next(); reportingChainEmails[i] = element.getAttributeValue("href").substring(7); i++; }
NOTE: The element.getAttributeValue("href").substring(7) is necessary because the email attribute includes a prefix, mailto:. - Retrieve and parse the Atom feed results for managed people. As explained earlier, a second call to the server is necessary to retrieve the coworkers of the chat partner (in other words, "the people my manager manages = my coworkers"). The code shown here in listing 10 looks very much like the code in the previous invocation.
Listing 10. Initialize Abdera parseruri = new URI(Activator.getDefault().getServerURI() + "peopleManaged.do?email=" + reportingChainEmails[1]); in = uri.toURL().openStream(); doc = parser.parse(in, Activator.getDefault().getServerURI());
- Locate the desired nodes in the people managed.
emailList = xpath.selectNodes("//*[@class='email']", doc);
in.close(); - Extract the required data from the selected nodes. Build the final list of coworkers (unless, of course, your chat partner is a department of one). As shown in listing 11, the chat partner is omitted from the list of managed people because there's no need to invite him to a chat he's already participating in.
Listing 11. Extract coworker email addressesif (emailList.size() > 1) { String coworkerEmails[] = new String[emailList.size() - 1]; i = 0; iter = emailList.iterator(); while (iter.hasNext()) { ExtensibleElement element = (ExtensibleElement) iter.next(); String s = element.getAttributeValue("href").substring(7); if (!s.equals(email)) { coworkerEmails[i] = s; i++; } } return coworkerEmails; }
Map coworker contact information into live persons
The last section mentioned that the InviteCoworkersToChatAction is notified of the job's completion through the ICoworkerLookupHandler.emailLookupsComplete method. This two-step approach involving a job is necessary because the search may take an indeterminate time during which the user interface cannot be blocked. When InviteCoworkersToChatAction.emailLookupsComplete is invoked, it is from a non-user interface thread, namely the same thread the CoworkerLookupEmailsJob was scheduled on. Therefore, it too executes on a non-user interface thread.
Your task now is to map the email addresses retrieved earlier into Lotus Sametime Person objects. These objects encapsulate the data (for example, ID, status) of a user logged into a Lotus Sametime community.
- Access Lotus Sametime's directory service. With each community, there are associated services. We're particularly interested in the directory services, which are queried as shown in listing 12 in InviteCoworkersToChatAction.emailLookupsComplete.
Listing 12. Access Lotus Sametime's directory serviceCommunity community = communityMgr.getDefaultCommunity(); RtcSession rtcSession = community.getRtcSession(); DirectoryServiceFactory dirSvcFactory = (DirectoryServiceFactory) community.getService(DirectoryServiceFactory.SERVICE_TYPE); DirectoryService dirSvc = dirSvcFactory.getDirectoryService(rtcSession);
RTC indicates Real-Time Collaboration. The RTC session represents the connection between your client and the community server.
- Given an email address, look up Lotus Sametime directory contact information. Looking up a Lotus Sametime person from his email address has the same type of constraint as searching the Lotus Connections Profiles server, specifically that the search time is indeterminate. Subsequently, your code must be structured to receive data as it is found using the Lotus Sametime's Lookup interface:
Lookup lookup = dirSvc.createUserLookup();
lookup.addLookupListener(new ContactLookupListener(emails, this));
lookup.lookup(emails);The LookupListener is notified as each directory lookup is completed with the outcome (resolved, failed).
- Bundle the contact information lookup request into one instance. Because the Invite Coworkers button does not present a dialog box until all the lookups are complete, let's define a LookupListener class to coalesce the results into a single packet as shown in listing 13.
Listing 13. Bundle the contact information lookup requestpublic class ContactLookupListener implements LookupListener { // variable declarations omitted public ContactLookupListener(String emails[], ICoworkerLookupHandler handler) { this.numEmails = emails.length; this.handler = handler; } public void handleLookupEvent(LookupEvent event) { if (event.getType() == LookupEvent.RESOLVE_SUCCESS) { contactInfos.add(event.getContactInfos()[0]); } else { resolveFailures++; } ...cont'd
Each time the Lotus Sametime directory resolves an email address to contact information (which includes the user's Lotus Sametime login and communities), the ContactLookupListener stores it away.
- Notify the requestor when all contacts are resolved as shown in listing 14.
Listing 14. Bundle the contact information lookup request (continued)// code excerpt continued from previous step if (numEmails == (contactInfos.size() + resolveFailures)) { handler.contactLookupsComplete((ContactInfo[]) contactInfos.toArray(new ContactInfo[contactInfos.size()]), resolveFailures); } } }
When the last resolution arrives, the handler InviteCoworkersToChatAction is notified through its contactLookupsComplete method.
At this point, you've successfully done the following:
- Added a button to the chat window toolbar (plugin.xml)
- Coded the methods to retrieve the chat partner's email address (InviteCoworkersToChatAction)
- Queried the email addresses of the chat partner's coworkers (CoworkerLookupEmailsJob) from the Lotus Connections Profile server while learning the importance of multithreaded application programming
- Mapped the coworkers' email addresses into Lotus Sametime contact information (ContactLookupListener) using the Lookup callback interface
In the next part, you write the code to map the contact information into live Lotus Sametime person objects with which you can initiate an n-way chat.
Put the pieces together and test
There are only two more steps to go:
- Mapping the contact information you queried in the prior section using the Lotus Sametime directory LookupListener interface
- Launching the "Invite Others to Chat" dialog box
After you've finished these bits of code, use the Eclipse plug-in development environment to run, test, and correct any errors.
Processing the retrieved contacts – threading issues
When it receives its last resolved contact, the ContactLookupListener notifies its handler, namely InviteCoworkersToChatAction, which implements the ICoworkerLookupHandler.contactLookupsComplete method. (NOTE: This method is implemented as part of this sample interface; it is not part of the Lotus Sametime API.) The InviteCoworkersToChatAction implementation is shown in listing 15 in its entirety.
Listing 15. Performing UI request from a non-UI thread
public void contactLookupsComplete
(final ContactInfo[] cis, int failedCount) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (!getShell().isDisposed()) {
getShell().setEnabled(true);
getShell().setCursor(
Display.getDefault().getSystemCursor(SWT.CURSOR_ARROW));
inviteToChat(cis);
}
}
});
}
|
The only code missing is the line inviteToChat shown in italics in listing 15. Only one line of code? What's all the ugly code around it?
Recall that the email lookup and contact information lookup were done on a background thread different than the user interface thread. This allowed the user interface to remain responsive, avoiding the warning from the operating system, "This application is no longer responding." The ugly code in listing 15 handles switching from the background thread to the user interface thread (Display.asyncExec(Runnable)).
Looking more closely at the code, you find that we have skipped some user interface housekeeping to focus on the salient points of extending Lotus Sametime. For example, before InviteCoworkersToChatAction starts the background job, it sets the window's cursor to the system wait cursor and disables the Invite Coworkers button. This gives the important feedback that the system is working on the user's behalf, and it also prevents impatient users from clicking the button again, thinking that they clicked incorrectly. The code wrapper above Display.asyncExec(Runnable) falls into the same user interface housekeeping category: Its purpose is to queue a block of code that needs to execute on the user interface thread. The code re-enables the Invite Coworkers button, returns the cursor to normal, and launches the Invite dialog box. This must all be done on the user interface thread because the graphic subsystem assumes that all invocations to it occur on this one thread (this is a common attribute of windowing systems to enable user behaviors like type ahead).
Adding the invocation of inviteToChat should have prompted a warning in the editor shown as a small red X and an idea bulb for canned solutions. You can click the light bulb and accept its suggestion to define a new method, or you can enter it manually:
private void inviteToChat(ContactInfo[] contactInfos) {
//TODO in next section
}
Processing the retrieved contacts - PeopleService
In the next snippet of code shown in listing 16, the class ServiceHub is used again. As its name suggests, it acts as a central location for services to register that other components then consume. You've already seen the ServiceHub used to retrieve the CommunityService; now you use it to retrieve another service provider, PeopleService.
Listing 16. Access PeopleService
private void inviteToChat(ContactInfo[] contactInfos) {
try {
PeopleService peopleSvc;
peopleSvc = (PeopleService)
ServiceHub.getService(PeopleService.SERVICE_TYPE);
|
The Community and People services are two examples of services that are provided with Lotus Sametime Connect. In more advanced usage, Lotus Business Partners have provided their own implementation of other predefined services such as voice chat services and video chat services. This pluggable services architecture allows more flexible construction of a solution from component parts provided by multiple vendors.
In the sample code, PeopleService provides the getPerson(...) API for mapping from a user's contact information (login) to their actual runtime representation in the Lotus Sametime environment.
Person person = peopleSvc.getPerson(id, communityId, isExternal);
The Person object returned can be a parameter in an invocation that creates a new chat conversation, for example, from contact info to live person.
Using the provided contact information elements that were retrieved by ContactLookupListener, the code in listing 17 maps them to their corresponding Person instances.
Listing 17. Retrieve person object from contact information
Person persons[] = new Person[contactInfos.length];
for (int i = 0; i < contactInfos.length; i++) {
persons[i] = peopleSvc.getPerson(contactInfos[i].getId(),
contactInfos[i].getCommunityId());
}
|
Open the Invite Others dialog box. Finally, you arrive at your destination, creating the Invite Others dialog box. This is accomplished using other methods of the PersonService shown in listing 18.
Listing 18. Initiate chat with chat partner's coworker(s)
if (persons.length > 1)
peopleSvc.createNwayInvite(persons);
else
peopleSvc.createConversation(persons[0]);
|
These last lines of code complete the example; now it's time to test.
At this point, your code is complete. Confirm that there are no errors shown in the Problems pane at the bottom of the Eclipse IDE; see figure 10.
Figure 10. Problems pane in Eclipse IDE
Correct errors as necessary. If you're unsure you've correctly coded part of a method, compare your project com.lotuslabs.invitecoworkers against the solution com.ibm.realtime.collaboration.sametime.sample.invitecoworkers. Although the two projects do the same thing, they have unique package names and plug-in identifiers, so they can be run simultaneously (of course, there would be two contributed Invite Coworker buttons in that case, which could be confusing).
If you prefer to try the solution without your plug-in, use the Open Project/Close Project pop-up menu choice. Eclipse takes into account only open projects when launching the runtime instance. See figure 11.
Figure 11. Choose active Eclipse projects by opening/closing them as needed
In figure 11, the solution project is open, and the article project is closed. Because closed projects are ignored, any errors are automatically removed from the Problems list.
The steps to run your project in the IDE are the same as those you followed in the earlier section, "Add the Invite Coworkers button to the chat window." Choose Run - Run, and then select the ST8.0 runtime launch configuration. If you need to debug a problem, set breakpoints by double-clicking in the left margin. A blue circle indicates the breakpoint; see figure 12.
Figure 12. Example breakpoint in Eclipse debugger
Choose Run - Debug instead of Run to launch the debugger.
After your code reaches a breakpoint, you can examine variable values and step through the code (HINT: F5 = step into method, F6 = step over method, F7 = step to end of method, F8 = resume). Figure 13 shows the Debug perspective with the debugger stopped in the InviteCoworkersToChatAction.run method. The Debug pane shows the execution stack, and the Variables pane shows the local variables of the selected method.
Figure 13. Debug perspective
You can even modify code on the fly. Make a code correction, and the method is recompiled into the running instance. If your breakpoint is midway through a method and you want to start from the beginning, select Drop to Frame from the Debug pane pop-up menu.
We hope you appreciate how the extensibility of Lotus Sametime Connect can lead to new and more productive ways of collaborating. It's easy to imagine how this simple example could be extended to take advantage of known relationships in your organization instead of only departmental relationships. For example, your company may have directory attributes for employees that indicate their areas of expertise. The same code you've just written can be modified to query these attributes and to present a custom Invite dialog box or to leverage other Lotus Sametime features such as instant meetings by presenting a custom dialog box.
| Name | Size | Download method |
|---|---|---|
| dw_projects_jan31.zip | 43KB |
HTTP
|
Information about download methods
Learn
-
Read the developerWorks Lotus article, "Extending IBM Lotus Sametime Connect V7.5."
-
Read the developerWorks Lotus article, "IBM Lotus Connections: Enhancing productivity with social computing."
-
Read the developerWorks article, "Getting to know the Atom Publishing Protocol, Part 3: Introducing the Apache Abdera project."
-
Refer to the Lotus Sametime technical content.
-
Refer to the developerWorks Lotus Sametime documentation page.
-
Learn more about Eclipse plug-in development.
-
Consult the Apache Abdera documentation.
Get products and technologies
-
Download the IBM Lotus Sametime Software Development Kit (SDK) from developerWorks Lotus.
-
Download the trial version of IBM Lotus Sametime.
-
Download the Lotus Expeditor Toolkit from developerWorks.
Discuss
- Participate in the discussion forum.
-
Participate in the developerWorks Lotus team blog.
Dan Kehn is a Senior Software Engineer at IBM in Research Triangle Park, NC. He has a broad range of software experience, having worked on development tools, such as Rational Application Developer, and on operating system performance, memory analysis, and user interface design. He is also co-author of the award-winning book, The Java Developer's Guide to Eclipse. Currently, he is a Technical Enablement Specialist helping business partners integrate their products into IBM Lotus Sametime Connect.
Comments (Undergoing maintenance)





