Documente Academic
Documente Profesional
Documente Cultură
Sren Peter Nielsen Volker Jrgensen Rami Menashes Tony Patton Marjorie Schejter
ibm.com/redbooks
International Technical Support Organization Working with the Sametime Client Toolkits December 2002
SG24-6666-00
Take Note! Before using this information and the product it supports, be sure to read the general information in Notices on page xi.
First Edition (December 2002) This edition applies to Lotus Sametime 2.5 and Lotus Sametime 3.0.
Comments may be addressed to: IBM Corporation, International Technical Support Organization Dept. TQH Mail Station P099 2455 South Road Poughkeepsie, NY 12601-5400 When you send information to IBM, you grant IBM a non-exclusive right to use or distribute the information in any way it believes appropriate without incurring any obligation to you.
Copyright International Business Machines Corporation 2002. All rights reserved. Note to U.S Government Users Documentation related to restricted rights Use, duplication or disclosure is subject to restrictions set forth in GSA ADP Schedule Contract with IBM Corp.
Contents
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii The team that wrote this redbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv Comments welcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvi Part 1. Introduction to Sametime client toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Chapter 1. Introduction to Sametime. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1 What is Sametime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.1 Community services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.2 Sametime Online Meeting services. . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.1.3 Sametime customization and integration services . . . . . . . . . . . . . . . 7 1.2 The Sametime client toolkits. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.1 The Sametime Links toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.2 The Sametime COM toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.2.3 The Sametime Java toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.2.4 The Sametime C++ toolkit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.3 Which toolkit to use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.3.1 Target platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.2 Required features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.3 Initialization load time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.4 Programming skills . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.5 Toolkit comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.4 The structure of the book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.4.1 The Sametime Community Server Toolkit Redbook . . . . . . . . . . . . . 15 1.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Chapter 2. Sametime toolkit services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.1 Sametime architecture basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.1.1 Proxy objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.1.2 The Model-View-Controller paradigm . . . . . . . . . . . . . . . . . . . . . . . . 20 2.1.3 Sametime events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.2 Interfaces and other funny words . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.2.1 Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.2.2 Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2.3 Listener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.2.4 Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
iii
2.2.5 Extend and implement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.3 Sametime services: What can I do with them . . . . . . . . . . . . . . . . . . . 29 2.3.1 General features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.3.2 Community service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.3.3 Awareness service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.3.4 Places service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.3.5 Lookup service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.3.6 Instant messaging service. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.3.7 Token service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.3.8 Storage service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.3.9 Names service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.3.10 Directory service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.3.11 Post service. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.3.12 Meeting services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3.13 Streamed media . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 2.4 The bare necessities for a client program . . . . . . . . . . . . . . . . . . . . . . 39 2.4.1 Create a Sametime session and load components. . . . . . . . . . . . . . 40 2.4.2 Login to the Sametime community . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.5 Core types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.5.1 Importing the core types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.5.2 Sametime ID types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.5.3 Sametime object types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.5.4 Sametime attribute types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Chapter 3. Places architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 3.1 What can I do in a Place with the client toolkits . . . . . . . . . . . . . . . . . 50 3.2 Place-based awareness and collaboration . . . . . . . . . . . . . . . . . . . . . 51 3.3 The Sametime Places model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.3.1 Virtual places. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.3.2 Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.3.3 Activities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.3.4 Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 3.4 Communication in a place . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 3.5 Permissions list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.5.1 Access to the place and the stage section . . . . . . . . . . . . . . . . . . . . 56 3.5.2 Places span the community . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.5.3 Place scalability. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.6 The Places APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.7 Examples of doing things in a place . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.7.1 Entering a place and listening for place events. . . . . . . . . . . . . . . . . 58 3.7.2 Figuring out which section is which . . . . . . . . . . . . . . . . . . . . . . . . . . 60 3.7.3 Changing your section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
iv
3.7.4 Getting your section and listening for section events . . . . . . . . . . . . 62 3.7.5 Getting a list of users in a section and listening to them . . . . . . . . . . 63 3.7.6 Setting attributes of place, section, user . . . . . . . . . . . . . . . . . . . . . . 64 3.7.7 Listening for changed attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.7.8 Sending messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 3.7.9 Listening for messages sent to you. . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Part 2. Java toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Chapter 4. Installation and setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.1 Installing the toolkit package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.2 Installing the IBM JDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.2.1 Setting up the path and classpath variables . . . . . . . . . . . . . . . . . . . 74 4.2.2 Compiling and running a JDK sample . . . . . . . . . . . . . . . . . . . . . . . . 76 4.2.3 Compiling and running a toolkit sample with the JDK . . . . . . . . . . . . 76 4.3 Setup of IBM VisualAge for Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 4.3.1 Importing the Sametime toolkit files into a project. . . . . . . . . . . . . . . 79 4.3.2 Setting up the classpath in VisualAge . . . . . . . . . . . . . . . . . . . . . . . . 82 4.3.3 Passing parameters to the applet . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 4.3.4 Modifying the java.policy file in VisualAge for Java. . . . . . . . . . . . . . 85 4.3.5 Running the applet within VisualAge. . . . . . . . . . . . . . . . . . . . . . . . . 86 4.3.6 Exporting the applet from VisualAge . . . . . . . . . . . . . . . . . . . . . . . . . 86 4.3.7 The resource not found problem. . . . . . . . . . . . . . . . . . . . . . . . . . . 87 4.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Chapter 5. Introduction to Sametime Java applets . . . . . . . . . . . . . . . . . . 89 5.1 Sametime applet basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.1.1 QuickStart Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.1.2 init() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 5.1.3 start() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.1.4 stop() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 5.1.5 destroy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 5.1.6 Using the LoginListener interface . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 5.2 Entering a place . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 5.2.1 STError: If something goes wrong . . . . . . . . . . . . . . . . . . . . . . . . . . 98 5.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Chapter 6. A place-based auction example . . . . . . . . . . . . . . . . . . . . . . . 101 6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 6.2 Objects in the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 6.3 Application flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.3.1 Initialization of the auction by the auctioneer . . . . . . . . . . . . . . . . . 107 6.3.2 Entering as bidder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Contents
6.3.3 Entering as on-looker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.4 Bidding on an item . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.5 Calling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.4 Client applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 6.4.1 The auctioneers application at a glance . . . . . . . . . . . . . . . . . . . . . 111 6.4.2 The customers application at a glance . . . . . . . . . . . . . . . . . . . . . . 113 6.5 The implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 6.5.1 Sametime services used . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 6.5.2 Class diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.5.3 Looking at the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 6.5.4 Launching and passing parameters to the applets . . . . . . . . . . . . . 122 6.6 Event flows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 6.7 Leveraging the places architecture . . . . . . . . . . . . . . . . . . . . . . . . . . 127 6.7.1 Sending text and data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.7.2 Changing attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 6.7.3 Receiving events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 6.7.4 Organizing messages, events, attributes . . . . . . . . . . . . . . . . . . . . 132 6.7.5 Other considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 6.8 What is next . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 6.9 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Chapter 7. Customized chat UI applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 7.1 Customized ChatUI Example 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 7.2 Important classes in the sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 7.3 CustomizeChatFactory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 7.3.1 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 7.3.2 CustomizeChatFactory2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 7.4 CustomizeChatUI2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 7.4.1 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 7.4.2 CustomizeChatUI2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 7.5 ImagePanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 7.5.1 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 7.5.2 ImagePanel.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 7.5.3 The HTML file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 7.6 Deployment considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 7.6.1 Integrating with a Domino application . . . . . . . . . . . . . . . . . . . . . . . 164 7.6.2 Loading the logo from a jar file . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 7.7 Extending the functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 7.7.1 RedStorageFrame.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 7.7.2 Changes in the factory class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 7.8 How to use with other toolkit UI elements . . . . . . . . . . . . . . . . . . . . . 182 7.9 Passing a token between applets . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 7.9.1 JavaScript for accessing the token . . . . . . . . . . . . . . . . . . . . . . . . . 189
vi
7.9.2 The HTML form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 7.9.3 Sharing information with other applets . . . . . . . . . . . . . . . . . . . . . . 192 7.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Part 3. C++ toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Chapter 8. Working with the C++ toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . 199 8.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 8.1.1 Modular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 8.1.2 Thread-safe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 8.1.3 Extendable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 8.1.4 Object-oriented API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 8.1.5 The toolkit services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 8.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 8.2.1 The toolkit package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 8.2.2 Background information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 8.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Chapter 9. A complex meetings sample . . . . . . . . . . . . . . . . . . . . . . . . . . 207 9.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 9.2 Sample architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 9.2.1 The MeetingUI class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 9.2.2 The MeetingController class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 9.2.3 The NWayChatUIDlg class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 9.2.4 The MeetingLauncher class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 9.2.5 The InviteUIDlg class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 9.2.6 The MeetingDlg class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 9.3 Preparing the MeetingUI class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 9.3.1 Initialize MeetingUI class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 9.3.2 Calling MeetingUI API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 9.4 Four steps to create the meetings . . . . . . . . . . . . . . . . . . . . . . . . . . 242 9.4.1 The invitation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 9.4.2 The place . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 9.4.3 Generating a token . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 9.4.4 Launch Sametime meeting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 9.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 Chapter 10. Using the C++ toolkit in Win32 programs. . . . . . . . . . . . . . . 297 10.1 The Win32Status sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 10.1.1 The sample code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 10.1.2 The output window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 10.2 Writing Win32 code with Sametime C++ toolkit. . . . . . . . . . . . . . . . 304 10.2.1 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 10.2.2 Using wmain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Contents
vii
10.2.3 The message loop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 10.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Part 4. COM and Sametime Links toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 Chapter 11. The COM toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 11.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 11.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 11.2.1 Accessing the toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 11.2.2 Installing the toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 11.2.3 Adding the COM toolkit reference to your project . . . . . . . . . . . . . 313 11.3 Visual Basic samples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 11.4 The Login sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 11.4.1 Initialize Sametime services - the SametimeSession module . . . . 316 11.4.2 Login to Sametime - the LoginForm . . . . . . . . . . . . . . . . . . . . . . . 317 11.5 The Awareness sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 11.5.1 Initialize Sametime service - the SametimeSession module. . . . . 321 11.5.2 Be aware of other users - the AwarenessForm. . . . . . . . . . . . . . . 323 11.5.3 Log into Sametime - the LoginForm . . . . . . . . . . . . . . . . . . . . . . . 332 11.5.4 Adding users to the AwarenessList - the AddUserForm . . . . . . . . 335 11.5.5 Changing my own status - the ChangeStatusForm . . . . . . . . . . . 338 11.6 The BuddyList sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 11.6.1 Initialize Sametime service - the SametimeSession module. . . . . 343 11.6.2 Adding Instant Messaging capabilities - the BuddyListForm. . . . . 345 11.6.3 Chatting with others - the ChatForm . . . . . . . . . . . . . . . . . . . . . . . 348 11.7 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 Chapter 12. The Sametime Links toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . 355 12.1 Toolkit features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 12.1.1 Awareness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 12.1.2 Instant messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 12.1.3 Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 12.1.4 Set status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 12.1.5 Place-based awareness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 12.1.6 Chat rooms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 12.1.7 Advanced JavaScript API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 12.2 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 12.2.1 Enabling a Web application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 12.2.2 Authentication considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 12.2.3 Adding a Sametime link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 12.2.4 A simple online assistance sample . . . . . . . . . . . . . . . . . . . . . . . . 366 12.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 Part 5. Appendixes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
viii
Appendix A. Some deployment considerations . . . . . . . . . . . . . . . . . . . . 371 Infrastructure for anonymous users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 Applet connections over the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 Instant messaging and data transfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 Appendix B. Working with the auction house sample material . . . . . . . 375 Installing the auction house sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 Looking at the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 Appendix C. Sample auction scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 The scenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 Entrance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 Auction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 Appendix D. Sametime portlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 Authentication by token . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 Using a Domino database as applet container. . . . . . . . . . . . . . . . . . . . . . . . 392 Opening the Java Sametime Connect client . . . . . . . . . . . . . . . . . . . . . . . . . 393 Place awareness portlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 PlaceAwarenessList applet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 Sample files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 Appendix E. Additional Web material . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 Locating the Web material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 Related publications . . . . . . . . . . . . . . . . . . . . . . IBM Redbooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . Referenced Web sites . . . . . . . . . . . . . . . . . . . . . . How to get IBM Redbooks . . . . . . . . . . . . . . . . . . . IBM Redbooks collections . . . . . . . . . . . . . . . . . ...... ...... ...... ...... ...... ....... ....... ....... ....... ....... ...... ...... ...... ...... ...... . . . . . 409 409 409 410 411
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Contents
ix
Notices
This information was developed for products and services offered in the U.S.A. IBM may not offer the products, services, or features discussed in this document in other countries. Consult your local IBM representative for information on the products and services currently available in your area. Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any IBM intellectual property right may be used instead. However, it is the user's responsibility to evaluate and verify the operation of any non-IBM product, program, or service. IBM may have patents or pending patent applications covering subject matter described in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to: IBM Director of Licensing, IBM Corporation, North Castle Drive Armonk, NY 10504-1785 U.S.A. The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time without notice. Any references in this information to non-IBM Web sites are provided for convenience only and do not in any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of the materials for this IBM product and use of those Web sites is at your own risk. IBM may use or distribute any of the information you supply in any way it believes appropriate without incurring any obligation to you. Information concerning non-IBM products was obtained from the suppliers of those products, their published announcements or other publicly available sources. IBM has not tested those products and cannot confirm the accuracy of performance, compatibility or any other claims related to non-IBM products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of those products. This information contains examples of data and reports used in daily business operations. To illustrate them as completely as possible, the examples include the names of individuals, companies, brands, and products. All of these names are fictitious and any similarity to the names and addresses used by an actual business enterprise is entirely coincidental. COPYRIGHT LICENSE: This information contains sample application programs in source language, which illustrates programming techniques on various operating platforms. You may copy, modify, and distribute these sample programs in any form without payment to IBM, for the purposes of developing, using, marketing or distributing application programs conforming to the application programming interface for the operating platform for which the sample programs are written. These examples have not been thoroughly tested under all conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability, or function of these programs. You may copy, modify, and distribute these sample programs in any form without payment to IBM for the purposes of developing, using, marketing, or distributing application programs conforming to IBM's application programming interfaces.
xi
Trademarks
The following terms are trademarks of the International Business Machines Corporation in the United States, other countries, or both: IBM eServer Redbooks (logo) Everyplace IBM Perform Redbooks SP VisualAge WebSphere
The following terms are trademarks of International Business Machines Corporation and Lotus Development Corporation in the United States, other countries, or both: Domino Designer Domino iNotes Lotus Discovery Server Lotus Notes Lotus Notes QuickPlace Sametime Word Pro
The following terms are trademarks of other companies: ActionMedia, LANDesk, MMX, Pentium and ProShare are trademarks of Intel Corporation in the United States, other countries, or both. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. UNIX is a registered trademark of The Open Group in the United States and other countries. C-bus is a trademark of Corollary, Inc. in the United States, other countries, or both. UNIX is a registered trademark of The Open Group in the United States and other countries. SET, SET Secure Electronic Transaction, and the SET Logo are trademarks owned by SET Secure Electronic Transaction LLC. Other company, product, and service names may be trademarks or service marks of others.
xii
Preface
This IBM Redbook is for developers and architects who want to utilize Sametime functionality in applications based on Java, C++, COM, or HTML and JavaScript. We explore capabilities offered by the different Sametime client toolkits, which you can use to add functionality to existing applications, and to create powerful new applications that enable real-time collaboration. The book is divided into four parts plus appendices. In the first part we present an overview of the different client toolkits and discuss which can be used for various tasks. We go on to introduce the Sametime community services and the concepts of event-based programming. A special focus area for our redbook is virtual places managed by Sametime, so we describe the architecture of Sametime Places as well. In the second part we discuss the Java Toolkit in detail. We describe how to install and set up the toolkit, both with a Java Development Kit (JDK) and with IBM VisualAge for Java, and we show the minimum code required to get a Sametime applet to run. We then present a place-based sample where we explore how to implement an online bidding application, and discuss topics like different ways to communicate in a place, access control, and so on. We complete the Java discussion by looking at ways you can create your own chat window to be used by other toolkit components. We also discuss different ways to deploy Sametime applets and show integration with a Domino database. The third part deals with the Sametime C++ Toolkit. In the main sample we build a full-blown Sametime Connect client by extending the BasicBuddyList sample from the C++ Toolkit. One of the techniques shown is how to launch the Java-based Sametime meeting client from a C++ program. We also discuss the different requirements when using Microsoft Foundation Classes (MFC) or Win32 API for Sametime C++ programs in this part. The newest additions to the Sametime client toolkits are discussed in part four: the Sametime COM Toolkit and the Sametime Links Toolkit. The COM Toolkit allows you to Sametime-enable any application that supports Visual Basic for Applications (for example, Microsoft Windows). You can also write a complete Sametime application in Visual Basic, which we do in our sample. We develop a BuddyList application that shows how to use most of the functionality offered by the COM Toolkit.
xiii
Sametime Links is a lightweight and easy-to-use toolkit. It is perfect for browser-based applications like e-commerce, CRM, help desk, and so on; and it does not require extensive programming skills. We give an overview of the toolkit and describe how easily it can add awareness and chat capability to an existing Web page. In the appendices we discuss some deployment considerations. We provide additional information about our online bidding sample, and we present two examples of Sametime portlets for WebSphere Portal Server. Most of the samples in this Redbook are available in source format from the IBM Redbooks Web site. Once you have read this book, we recommend that you explore what you can do with server-side Sametime programs in the redbook Working with the Sametime Community Server Toolkit, SG24-6667.
xiv
Tony Patton is an application developer in Louisville, Kentucky. He has worked with Lotus Notes/Domino for over five years. Tony is the author of two books from Manning Publications: Domino Development With Java and Practical LotusScript. He is also a frequent contributor to industry publications. Marjorie Schejter works at the IBM Haifa Software Lab in Rehovot, (Ubique) Israel. She has over 15 years experience in the high tech industry. For the past several years she has specialized in the Sametime Toolkits, both as a technical writer and product manager. We want to extend a special thank you to the following people that have contributed greatly to the book with written material, samples and managerial support. Assaf Azaria works at the IBM Haifa Software Lab in Rehovot, (Ubique) Israel. Assaf has been a member of the Sametime team for more than four years, and is one of the architects of the Sametime server and client toolkits. Harry Hornreich has been development manager for the Sametime Toolkits and Clients in the Haifa Software Lab in Rehovot, (Ubique) Israel, since August 1997. In this role he has driven the development of the Java, C++, Sametime Links,COM and Community Server Toolkits. In addition he is responsible for the Sametime Connect clients which are built on top of these Toolkits. Harry has a M.Sc. in Computer Science from the Technion in Haifa and has more than 12 years experience in software development. Amir Perlman is the team leader for Java technologies at the IBM Haifa Software Lab in Rehovot, (Ubique) Israel. For the last three years he has participated in architecting and developing the Java Toolkits, Sametime links, and the Java connect client. We also extend a special thank you to Jeremy Dies, Lotus Sametime brand manager, and Don Bunch, Lotus Sametime product manager, for their help and support. In addition we send thanks to the following people for their contributions to this project: Christopher Baker, IBM Westford Software Lab Tammy Delk, Lotus Software, Lexington Kentucky Dvir Landerer, IBM Haifa Software Lab, Israel Barry O'Nan, Lotus Software, Lexington Kentucky Yaron Reinharts, IBM Haifa Software Lab, Israel Haim Schneider, IBM Haifa Software Lab, Israel
Preface
xv
Yafit Sami, IBM Haifa Software Lab, Israel Christian Steege, Lotus Professional Services, IBM Switzerland William Tworek, ITSO Team Leader, Cambridge, Massachusetts Alison Chandler, ITSO Poughkeepsie, New York
Comments welcome
Your comments are important to us! We want our Redbooks to be as helpful as possible. Send us your comments about this or other Redbooks in one of the following ways: Use the online Contact us review redbook form found at:
ibm.com/redbooks
xvi
Part 1
Part
Chapter 1.
Introduction to Sametime
In this book we explore the use of the Sametime client toolkits. Our primary target audience is developers and architects, but administrators may also find parts of the book useful. Please note that this book covers examples of toolkit use. Given the rich functionality of the Sametime toolkits, we cannot cover all services and APIs in detail in a single redbook. Instead, we have taken selected examples and explored those in detail. See 1.4, The structure of the book on page 12 for an overview of the examples we cover. Note: The Sametime COM and the Sametime Links toolkits described in this book were not released as final products at the time of writing. There may be minor differences between the described features and installation procedures in this book and those for the released toolkits. In this chapter we start by giving a brief overview of what Sametime really is. Sametime provides services in the areas of instant messaging, online meetings and customization/integration. We discuss these areas and then focus a bit more on the toolkit capabilities of Sametime. We conclude the toolkits overview with a table where we summarize the features and considerations for each of the Sametime client toolkits. Finally, we describe what is covered in the rest of this book. Parts of this introduction are adapted from the Lotus White Paper Real-time Collaboration with Lotus Sametime, Part No. CC7MANA.
Sametime can also provide awareness to customers and business partners outside the firewall through integration with America Onlines Instant Messenger (AIM) service. With the Sametime Connect client, users can exchange instant messages with any AIM user. (Administrators determine whether this Sametime capability is enabled.) Sametime even enables users to replace obscure AOL names with more meaningful nicknames. Once users are aware of who is online, they can initiate interaction simply by sending an instant message. A user might start an instant message, an online meeting, a telephone callwhatever suits the task at hand. For example, an instant message is an efficient, low-bandwidth medium for the quick clarification of an idea; but to explain the details of a design specification, a phone call may be a more appropriate medium. Of course, nobody wants to be available for spontaneous communicationread interruptionall the time. For this reason, Sametime gives each user full control over their availability. Levels of participation include Active (online and available), Away (offline or otherwise unavailable) and Do Not Disturb (online but unavailable).
Sametime allows any user to share any program from his or her desktop, such as word processors, spreadsheets, and project management software. Other participants are not required to have the same software in order to participate and see whats being shared. When appropriate, users can also pass control of the application back and forth as necessary; the initiator can reassert control at any time. Sametimes shared whiteboard is the online equivalent of a typical whiteboard in an office or classroom. Users can draw on it, show presentations, and annotate documents on it. Sametime also converts popular file types into pages for convenient display during whiteboard sessions. As noted previously, Sametime fully supports both ad hoc and scheduled meetings. Online meetings can be anything from a quick show me session among two people, to team briefings on a new product, to a full-scale virtual seminar involving hundreds of participants across both the WAN and the Web. Meeting information is posted in a server-based Meeting Center, along with agendas and preparatory materials. Invitees can access these materials anytime before, during, or after the meeting. For maximum convenience and to eliminate barriers to off-site invitees, users can participate in online meetings and whiteboard sessions directly from Web browsers, without downloading and installing special software or plug-ins. Users can also specify the type of meeting
to help manage bandwidth. For example, a user can have a meeting that is designed to allow several people to collaborate on a specific application. Sametime also allows a user to set up meetings that are designed for one presenter and a large audience of observers, like an organizational or earnings announcement. The meeting moderator decides which services (chat, whiteboarding, audio/video, etc.) will be available to each participant. In this way the user easily customizes the meeting based on their goals and collaboration needs.
1 Only with Microsoft Internet Explorer. 2 Only Community Services are supported. UI and Meeting components were only tested on Windows.
COM X
Java X X X X
X
C++ X X
10
The following table notes the programming skills required for each of the Sametime client toolkits.
Table 1-4 Programming skills required for different Sametime toolkits
Toolkit Sametime Links COM C++ Java Required skills HTML/JavaScript Windows Visual (COM) C++ Java
This concludes our description of the four main factors to consider when deciding which toolkit to use for development of Sametime client applications. We have discussed them in the order you should consider them, if possible. However, your special situation will decide the right order.
Easy to use Basic awareness Light weight (~20K and instant applet) messaging No install Easy to use Does not affect page Works in any layout environment that Customizable supports COM HTML-dialogs ADA1 -compliant
Example uses
Web-based buddy BuddyList e-commerce list app application in Help desk e-commerce Visual Basic Add awareness and application with messaging to Web Add awareness and meetings messaging to MS pages and Office applications Browsers on Windows2 Any Windows application that supports COM Java applets and applications
Target environment
11
Sametime Links Limitations and considerations Does not expose all functionality Less control Currently only supported on Windows Any Web development environment HTML and basic JavaScript
COM
Java
C++
Comparatively large No UI Only on Windows Only basic features, Appshare host and No meeting services A/V only on no toolkit user or A/V4 Windows interface UI is not components ADA1- compliant Any development environment that supports COM5 Visual or scripting language Any Java development environment that supports JDK 1.1 Java programming skills Microsoft Visual C++ 6.0 Developer Studio SP5 C++ programming skills
Development environment
Sametime 1.5 and Sametime 2.6 and 2.x above A standalone add-on installation will be provided for Sametime 2.5 as well
1 2
Sametime 2.x Sametime 2.x Sametime 1.5 Sametime 1.5 can community service also be used, but can also be used for community (though not places services only (not or storage) places or storage)
Americans with Disabilities Act Win95/98/NT/2000/XP, with MSIE 5 and above, and Netscape 4.7 and above 3 Microsoft Foundation Classes 4 Though the Meeting Room can be launched from a C++ application 5 For example, Visual Basic/VBA (Microsoft Office), LotusScript in Lotus Notes, and JavaScript in Microsoft Internet Explorer
12
In Chapter 3, Places architecture on page 49 we focus on one particular toolkit service, PlacesService, and describe the Sametime Places architecture with emphasis on what you can do from a client point of view. We also show you the event flows for some common operations in a Sametime place. Java toolkit Chapter 4, Installation and setup on page 71 describes how to get the Sametime Java toolkit and how to set it up to develop and run programs with either the IBM Java Development Kit (JDK) 1.3 or IBM VisualAge for Java 3.5. Chapter 5, Introduction to Sametime Java applets on page 89 contains a skeleton Sametime applet where we describe which Sametime code should be placed in the applet events: init, start, stop, and destroy. We included this chapter to give you a better background for understanding the sample in the next chapter which is based on two applet clients. Chapter 6, A place-based auction example on page 101 is our first large example. It describes how we solve the challenge of having an on-line real-time auction. The sample utilizes the Sametime PlacesService. We show how place members can communicate both by direct messages and by changing attributes, and much more. In Chapter 7, Customized chat UI applet on page 137 we look mostly at a new user interface component introduced in the Sametime 2.5 Java toolkit that allows the developer to customize the look and feel of the ChatUI component. In the first iteration we show how you can add buttons with canned chat messages, add your own logo to the window, and so on. We discuss the different ways you can deploy Sametime applets and walk you through the steps required to deploy via a Domino database. Then we take the sample further by using the Sametime storage service to save our canned messages between invocations, and finally we show how we can launch a new Sametime applet in our browser without requiring the user to authenticate again. This is accomplished by utilizing the Sametime TokenService. C++ toolkit The part covering the C++ toolkit starts with Chapter 8, Working with the C++ toolkit on page 199, where we describe how the get the Sametime C++ toolkit and how to set it up. In Chapter 9, A complex meetings sample on page 207 we have a large example where we basically end up with a client that matches the Sametime Connect client. We start out with a sample from the Sametime C++ toolkit and then add functionality to it. We describe the new classes introduced and discuss four steps to create a meeting, from the initial creation of an invitation to the launching of the Sametime Java Meeting client. Even if you are not a
13
C++ programmer, you can learn some interesting things about how to work with Sametime places in this chapter; and if you are a C++ programmer, it should also interest you to see how to launch the Java-based meeting client from a C++ program. Finally, in Chapter 10, Using the C++ toolkit in Win32 programs on page 297 we describe what is different if you want to develop a Win32 program with the Sametime toolkit (in contrast to using the Microsoft Foundation Classes (MFC) which we used in the prior chapter. COM and Links toolkits The last part covers the newest Sametime client toolkits. In Chapter 11, The COM toolkit on page 309 we show how you can build a Sametime BuddyList using Visual Basic. First we describe how to get the COM toolkit and to enable it in Visual Basic. This process is split into three parts, starting with the basic capabilities of logging in and out from the Sametime server, adding awareness of other users, and finally adding the capability to chat with those users. In this sample you can see how you can use the different capabilities of the Sametime COM toolkit to add real-time collaboration abilities to programs like Microsoft Office and your own Visual Basic programs. Finally, Chapter 12, The Sametime Links toolkit on page 355 is about the newest Sametime client toolkit. The Sametime Links toolkit allows a Web page developer to add real-time collaboration capabilities to any Web page without any particular Sametime knowledge. The toolkit only requires HTML and JavaScript skills. A beta version of the Sametime Links toolkit has been available for some time, but the new version has been totally changed. At the time of writing the toolkit was still in development, so we have couched our descriptions of many of the toolkit functions in general terms; the final toolkit may have small differences compared to our description. In addition to these four parts we have some appendices in the book. We start with a few deployment consideration we had during the work on this book. Then we describe how you can set up the sample files we described in Chapter 6. In another appendix we take you through a scenario with screen captures using the same sample, and finally we have an appendix where we show an example of how Sametime can be used in portal solutions. The appendix contains description of two Sametime portlets that run under WebSphere Portal Server.
14
1.5 Summary
In this chapter we have given you an overview of the services provided with the Sametime server product. We have briefly described the various Sametime client toolkits and discussed which to use when. Finally, we have described the structure of the rest of the book.
15
16
Chapter 2.
17
Distributed
Client/Server
Event-handling
18
Client Application
Sametime Toolkit
Sametime Server
There is much more to the Sametime server architecture, but here we focus on what you need to know from a client program point of view. To learn more about the Sametime server architecture see the redbook Working with the Sametime Community Server Toolkit, SG24-6667.
19
Client Application
User Proxy Object Application Logic Place Proxy Object
Toolkit
Sametime Server
Community Service
User Object
Places Service
Places Service
Place Object
It is important to note that in Sametime the server-side object also can send events to the client applicationthis means that the server-side object can notify all clients who are having a proxy object. As you see, Sametime keeps the developer well away from handling the connection to the original object, but it is still important always to remember what happens in the background.
20
The component types are interconnected, as shown in Figure 2-3. Here, connected means that the objects know of each other. Typically, this means: The model maintains a list of listeners - objects that are interested to know if the model is changed; for instance, if an attribute is updated. If this happens, the model notifies all listeners of the event. The views store a reference to their model, and interact with its controller. The controller stores a reference to its view.
Controller 1
Controller2
View 1
View 2
Model
If the model is updated for some reason, all listeners are notified. All views update themselves if the update is relevant. (The update may concern an attribute not shown by the view at all, and so the view does not need to be updated.) The other way of interaction is that the controller catches an event from the operating systems I/O devices, like a mouse click. In this case it evaluates, together with the view, whether the event needs to be handled, and if so, it calls an appropriate method of the model. The Sametime Java toolkit is designed according to the MVC paradigm. It provides non-visual classes, for instance a Place which a Sametime application can enter. It also provides visual elements, for instance a PlaceAwarenessList, which can easily be associated with a place. The PlaceAwarenessList delivers, by default, a ChatController that allows instant messaging with the users in the
21
list. You can always exchange this controller with another controller, for instance an AVController, which allows you to run instant meetings, including Audio/Video conferences, if the server has the features installed. Figure 2-4 shows the Sametime interpretation of MVC.
PlaceAwarenessList 1
PlaceAwareness.List 2
Place
Sametime Server 1
Sametime Server 2
It is important to reiterate that the Sametime architecture is distributed: the place watched by the PlaceAwarenessList is on the server, while the views are on the client computers (usually on different ones). If you use the PlaceAwarenessList from the Sametime toolkit and bind it to your Place proxy object, it will automatically watch the place, that is, it will show the users in the place and allow you to initiate conversations with them.
22
Also, events from the server may arrive in a different sequence than the actions sent to the server occurred. This is an important feature, since some actions on the server may take longer than others. The events are categorized into interface definitions associated with the services they are related to, called Listeners. You will see in the remainder of this book that there are several services to use and they all bring interfaces to be implemented. For a simple implementation example of how this works, see 5.1.6, Using the LoginListener interface on page 94. However, the use of interfaces and listeners may seem more complex in other examples, so in the next section we explore the concepts of events, interfaces, listeners, and adapters in more depth.
2.2.1 Event
An event is something that happens during the execution of an application and that may alter the course of what happens next: a key is pressed, the mouse is clicked, a Sametime user sends you a message, and so on. Events do not necessarily come directly from the user and new events can be defined by the programmer. The Sametime toolkits provides a lot of events that you can (or rather must) use in your programming. When you request a service from Sametime, or issue a command against Sametime, in most cases you do not get an immediate reply (a return code, a handle, or similar) as you do with a remote procedure call. The reply is delivered by an event. You will only get this event if you have subscribed
23
to the group of events that the required event is part of. Thus to know whether the things you do succeed or fail you must subscribe to events. To get handles to other Sametime objects (like sections in a place you just entered) you must subscribe to events, and so on. For example, to know whether your login attempt to the Sametime server was successful you must subscribe to the login events, which either can be loggedIn or loggedOut (which you also get if a login request was refused). Another example is if you enter a Sametime place. To know you have entered successfully and to get a handle to your own section in the place, to get a handle to yourself in the place, and so on, you need to receive the entered event. However, to get this event you must subscribe to the full group of Place events (which number 17 in all) and you have to handle them all. This may just mean that you a have a method that does nothing for each of those event messages that you have no interest in, but you still need to have those methods or your program will break. The question is then: How do you know which event messages you need to implement methods for? You can, of course, look in the documentation, but there is a better way to make sure that your class implements all required methods. That is by using an event interface. We describe what an interface is in the next section.
2.2.2 Interface
When you work with the Sametime Java and C++ toolkits, you will be working with interfaces. Some books talk on a high and abstract level about interfaces as a way to implement something that is better than multiple inheritance in object-oriented (OO) programming. However, we will keep our discussion of interface on a pragmatic level and try to illustrate how interfaces are used in a Sametime context. First, lets look at the general definition of interface. Definition: An interface is a named collection of method definitions (without implementations). An interface can also declare constants. An interface gives a list of prototypes for methods that must be provided by any class that implements the interface. An interface does not implement methods, it only defines them. A class that implements the interface agrees to implement all the methods defined in the interface, thereby agreeing to certain behavior. (This is what some teachers of OO programming call making a contract.)
24
Lets go back to the example we discussed in 2.2.1, Event on page 23 where we needed to make sure that we had methods for all the possible event messages we might receive when we subscribe to Place events. We handle this by implementing in our class a PlaceListener interface provided by the Sametime toolkit. When we decide to implement an interface in our class, the compiler makes sure that we really also have created methods for all the method definitions provided by the interface. A class can implement as many interfaces as the programmer wants. For example, a single class can listen to both Place, Section, and UserInPlace events. All that is needed is that the class implements three different Sametime Listener interfaces. You can also think of an interface as a language. If a class implements three interfaces, any object that wants to talk to it must at least talk one of the three languages. By implementing the PlaceListener interface, our class can handle any event that may come when it subscribes to listen to a place. Our class thus gets the capabilities of a PlaceListener; in other words, it becomes a PlaceListener. A class that implements a certain Sametime Listener interface becomes a Listener of that certain type. This leads us to the topic of the next section, Listeners.
2.2.3 Listener
A listener is an object that listens for particular sorts of events. Listeners must be instantiated from classes that implement listener interfaces. Sometimes, depending on the complexity (amount of event messages) of the interface, the listening responsibility is added to a class that has other responsibilities as well. Sometimes a class is dedicated solely to the handling of the messages defined by the listener interface. Such a class typically extends an Adapter class, which we will discuss in the next section. Lets discuss listeners in a Sametime context first. If a class in your application is to be a Sametime event listener, it must do the following: Implement a Listener interface for the group of Sametime events it wants to listen to. For example, the class responsible for logging into the Sametime community must implement the LoginListerner interface. To do this the class must contains definitions for the methods loggedIn and LoggedOut. Subscribe to be notified about the group of Sametime events.
25
For example, to be notified about Login events, the class instantiation (or another object) must send the addLoginListener message to the community service object in Sametime and pass the object that must receive the events (our class instantiation) as a parameter. (To stop listening to Login events you must use the message removeLoginListener and pass the same object as a parameter.) As we discussed previously, a class can implement many interfaces and thus also listen to several groups of Sametime events. While it may make sense to keep the logic that handles various Sametime events in one class, the class may become very large because of the requirement that it implements all methods defined in all the Listener interfaces it has to subscribe to. In such cases, listening to the different groups of events can be delegated to objects called Adapters, which we discuss in the next section.
2.2.4 Adapter
A class that implements an interface must provide code for all the methods in the interface. This can be a lot of dull work in cases where you only are interested in a few of the event messages. Adapters provide dummy bodies that do nothing for all the methods in a particular interface. If a listener inherits from an adapter, it need only provide code for the appropriate methods, leaving the default bodies for the others. The Sametime Java and C++ toolkits provide Adapter classes to match all the Listener interfaces defined. To illustrate the advantage of Adapters, lets ponder an example where you have entered a Sametime place in your application. Your application includes a user interface class which has the responsibility of displaying the value of some of the place attributes. To be notified about the value of place attributes and when they change you need to implement the PlaceListener interface and subscribe to Place events. If you want your user interface class to listen directly for the attribute values (sent by the attributeChanged message), the class also has to handle the 16 other messages that may be generated by the place. To avoid cluttering your user interface class with 16 do-nothing methods, you can create another class to handle the listener responsibility. If you create this class as a subclass of the PlaceAdapter supplied by the Sametime toolkit, all you need in your subclass is a constructor, the attributeChanged method and a variable to keep a reference to the user interface object. You do not need to define methods for the other 16 event messages because they are defined in the PlaceAdapter superclass.
26
You can then use the addPlaceListener method against the place and pass your new adapter class as the parameter. When the adapter class receives the attributeChanged method, it can either pass it directly on to the user interface object that it has a reference to or it can do some processing on the received event before passing it on. Using an adapter in this example, you get the following advantages: The class with your application logic or user interface logic does not have to be cluttered with a lot of do-nothing methods to handle events you not are interested in. You do not have to implement the Listener interface in your adapter class since this is handled by the superclass. The adapter subclass only has to define methods for events it wants to process. All other events are passed on to the adapter superclass for default processing. The developer can immediately see which events in a group are handled by the listener and which are ignored. You can choose between simply having your adapter class pass the interesting messages on or you can do some processing in your listener. For example, you can handle the attributeChanged message in your listener, determine which attribute is changed, and only pass the event on if it is a subset of attributes that you are interested in. In such a case you can also let the adapter invoke different methods for different attributes in your receiving object. On the flip side you get more classes in your application when using listener classes that inherit from adapters. It may be harder for the maintenance developer to understand the relationships between the classes. In Java, one way to make it easier to see which class a listener class serves is to use inner classes.
27
If you define your listener classes that inherit from adapters as inner classes of the class they server, you get both the advantage that it is easy to see that the classes relate to each other, as well as the fact that the inner class gets direct access to the variables and methods of the containing class. To see an example of this in action you can study the Sametime 2.5 Java toolkit sample named ChatMeeting. The main class is also named ChatMeeting and it has the following three inner classes: CommunityEventsListener This class implements the LoginListener interface and handles the login events for the ChatMeeting class. When it receives the loggedIn message, it in turn invokes the enterPlace() method in the containing class. This class does not extend an adapter because the interface only defines two messages which both need to be handled. ParticipantListListener This class extends the AwarenessViewAdapter class. It keeps track of the number of users in the chat and updates variables in the containing class when the number changes. PlaceEventsListener This class extends the PlaceAdapter class. It only handles the entered message out of the 17 possible Place events, and it does so by invoking methods on objects referred to by variables in the containing class. By using separate classes that extend the AwarenessViewAdapter and the PlaceAdapter, we have been saved from having to implement 21 do-nothing methods required by the corresponding interfaces. In addition, by making the classes inner classes of the class that uses them, it is easier to see the relationship between the classes. This concludes our discussion of interfaces and other concepts related to the handling of Sametime events. Before moving on we also want to look at the meaning of extend and implement when used in Java.
Extend
Extend is Java-speak for subclass. In Java the class that is extended is the superclass for your class. Java has single inheritance and thus there is only one class that your class extends.
28
Implement
The implement keyword in Java is followed by one or more Interfaces. You can only implement interfaces in your class. For each interface you state that your class is implementing the Java compiler check whether you really have implemented all methods defined by the interface.
29
STSession
STCompAPI
StreamedMediaBroadCastFactory
Meeting Services
Core Types
Community Services
Figure 2-5 Sametime toolkit services
Note that all services are not supported by all Sametime client toolkits. Refer to 1.2, The Sametime client toolkits on page 7 for information about which services the different toolkits support. The Java toolkit also provides Community User Interface components (not shown on the figure).
30
Your application can listen to the service and so receive events when the service becomes unavailable or is available again. You will use this if you decided to sense for service availability as described in the preceding paragraph. The other methods of a service interface are performing special tasks, which we describe in the following sections.
31
Sametime itself uses the attributes, as you can see in Figure 2-7. The Tools dialog is used to set your attributes so that other users can see, for example, if you have a video camera or not. Another use of attributes is shown in Figure 2-8 on page 33, where the buttons in the connect client reflect the availability of meeting and multimedia services at the server. Sametime connect shows only the features installed.
32
Note that attributes are automatically filtered on the server for performance reasons, so your application has to modify the filter to receive the relevant attribute updates using AttrFilter. To watch a list of users you just create a watch list using createWatchList(), add the objects to watch using addItem() and you are done. But dont forget to add a StatusListener so that you receive the updates from the server. You also can change your own attributes using changeMyAttr().
33
34
Sametime uses the storage service to store your buddy list on the server and allows users to do roaming. A very good example of the use of the storage service is the Buddy List sample application in the Sametime 3.0 Java Toolkit. We also show an example of using the storage service in 7.7, Extending the functionality on page 173. You should avoid making your application completely dependent on the storage service; the application should have acceptable defaults to work independently in case the storage service is not available. You also should not store large amounts of data using this service because it is not designed and optimized for this.
35
To do this, you can use the directory service. First ask the directory service for the list of directories using queryAllDirectories(), and then work with the received directory object. With setMaxEntries() you define the number of entries you want to read (remember, directories are usually very large). The Sametime Java toolkit offers a default UI for directory browsing, so you often do not have to implement this at all. Figure 2-9 shows how the UI looks. The Java Toolkit contains the Directory Panel sample, giving you insight on how to use this service.
Sametime 3.0 supports two types of directories directly: Domino and LDAP. A Domino directory can be accessed via LDAP also. Depending on the directory configuration, directory browsing might not be available on your Sametime server. For example, directory browsing is not available when using an LDAP directory. When directory browsing is not available, the developer should limit the application to the use of the Lookup service (see 2.3.5, Lookup service on page 33) that provides name-resolving capabilities.
36
A typical use is to notify a list of users (you already have) of an event. It can be used, for instance, to remind the attendees of a meeting to attend now, or even to invite them to a meeting, as shown in Chapter 9, A complex meetings sample on page 207. Remember that IMs are created locally, so your network connection will be used to carry out the channel creations. You may want to consider using a place, but this means that you have to make the users enter the place before you use the post service.
37
Whiteboard service
38
This concludes our walkthrough of the Sametime services. The services offered obviously map to the architecture of the Sametime server. However, since we concentrate on the Sametime client toolkits in this book, we will not look into the internal architecture of the Sametime server.
39
take a look at the source code. We will walk you through a Login to a Sametime community and illustrate it with code snippets from the Java sample Login program. If you want to see all the Java source code in context, refer to Chapter 5, Introduction to Sametime Java applets on page 89, where we walk through the structure of a basic Sametime-enabled applet.
Each Sametime session must have a name. Create a string by appending an object identifier to the string LoginApplet. However, if the session name given turns out to be in use already an exception will be thrown. If creating the Sametime session goes well, you must load the components (or services) that you want to use in your application into the Sametime session. Take the easy way out in this basic sample and load all community services components:
m_session.loadAllComponents();
The documentation provided with the Sametime Java and C++ toolkits discusses how to load individual components. Once the necessary components have been loaded you can start the Sametime session:
m_session.start();
Later on, when our application is stopped, we also need to stop the Sametime session and unload it. We are now ready to log in.
40
m_comm = (CommunityService)m_session.getCompApi(CommunityService.COMP_NAME);
The STSession.getCompApi() method requests a reference to a component, based on its name. Every Sametime component has a COMP_NAME field used for this purpose. The name of the STBase component is CommunityService.COMP_NAME. The returned component reference needs to be cast to the right type, since STSession does not distinguish between components and does not know the type of the requested component.
Adding a LoginListener
Because you are connecting to a remote community, the login process can take time. It involves initiating the connection to the server, sending authentication data, and waiting for confirmation. To avoid waiting for the confirmation, you can ask the Community service to notify you when the login procedure is finished. Do this by adding a login listener before attempting to log in:
m_comm.addLoginListener(this);
Logging in
You are now ready to log in. You can try to log in as an authenticated user by passing a user ID and password, or by passing a token. You can also attempt anonymous login. This is an example of logging in by user ID and password:
m_comm.loginByPassword(getCodeBase().getHost().toString(), getParameter("loginName"), getParameter("password"));
If your login attempt was refused, you would receive the loggedOut event, together with an reason code that explains why you could not log in. Besides the community service, your basic Sametime program also needs to import the Sametime core types like STUser. This concludes the bare necessities that (almost) any Sametime program must go through to start utilizing the Sametime services, but we explain more about the Sametime core types in the next section.
41
42
STId
STLoginId
Figure 2-10 Sametime IDs class diagram
These classes are mainly for internal use and are not exposed at the API level. The Sametime API uses more high-level entity definitions that encapsulate the Sametime ID within.
STId
STId is a Sametime entity identification. Every Sametime entity has its own unique persistent ID within the Sametime community. The format and content of the ID may vary among different communities and is dependent on the companys directory that is integrated with Sametime (Domino, LDAP).
STLoginId
STLoginId represents a Sametime entitys runtime session identification. While all Sametime entities have their STId, some of them will also have session IDs assigned to them during runtime. For example: A user has its own fixed ID (for example, John Smith/IBM). When the user logs into Sametime, a Login ID will be assigned to him (multiple concurrent logins will result in different login IDs). This information can be used to access a specific login of the user and is valid only for the duration of the login. See the Java or C++ toolkit Tutorials for more information on the Sametime user model.
43
STObjectImpl
STObject
STGroup
STUser
STServer
STUserInstance
Figure 2-11 Sametime entities class diagram
STObject
The STObject interface/abstract class (the former for Java and the latter for C++) provides a general definition for a Sametime entity. Every Sametime entity must implement this interface/abstract class. The definition of a Sametime entity is made up of three parts: Sametime ID Every Sametime entity is uniquely identified by its ID. Name The entitys name. Description Additional descriptive information for the Sametime entity.
STObjectImpl
Currently there are three Sametime entities defined: STUser, STGroup, STServer. Since the implementation is common for all these classes, we added the STObjectImpl, which is used as the base class for the classes implementing the STObject interface/abstract class.
44
STUser
STUser represents a Sametime user in the community. Since Sametime provides user-to-user interaction, this is the most commonly used structure. In order to interact with other users in the community, the application will need to obtain a valid STUser object. This can be done in one of three ways: Start with a user name and search (resolve) for the matching STUser object using the LookupService. Look through the directory and locate the requested user and its matching STUser object. This functionality is provided through the DirectoryService API. Create the STUser object from scratch. This is an advanced option and requires knowledge of the specific ID allocated for the user. This information may vary among different Sametime installation environments and therefore should only be used by advanced developers who have intimate knowledge of the directory used by Sametime. Once the STUser object is available, the application can perform user-to-user interactions such as: watch a users online status, send instant messages, invite to meetings. Note that some of the services, such as the Awareness service, extend the STUser base class and provide service-specific properties and APIs. For example: STWatchedUser that is provided by Awareness service adds awareness data (online status and attributes) about the user.
STGroup
STGroup represent a Sametime group (also know as a public group in the Sametime Connect clients) as it is defined in the directory. This object can be created in the same ways as the STUser object: search by name (resolve), browse the directory, or create from scratch.
STServer
STServer represents a Sametime server. Once a user logs into the community, the user is connected to a specific Sametime server. The STServer object can be obtained through the getServer() call of the Login object. A typical use of this object would be to monitor its attributes using the Awareness service. Server attributes are used to publish its capabilities throughout the community to all of its clients. For example, whether audio/video capability is enabled by a specific server is published as a server attribute. Clients can use this information to determine the kind of meeting capabilities supported by the current server installation.
45
STUserInstance
STUserInstance represents a single login of a Sametime user. Once a user is logged into Sametime, he is assigned an STLoginId which is valid for the duration of the login. An application can choose to use this information, which is contained by the STUserInstance object, to contact a specific login of the user instead of relying on the server for choosing the appropriate login for it. While this information is available to the developer, we recommend not to use or rely on it due to its transient nature, as opposed to the STUser object which holds persistent information.
STAttribute
STExtendedAttribute
Figure 2-12 Sametime attributes class diagram
Attributes are used widely in Sametime in the description of Users, Servers, and Places. We discuss the two different types of attributes here.
STAttribute
STAttribute is the basic definition for a Sametime attribute that is comprised of two parts: Key: A number associated with the attribute. Well-know keys are allocated and managed by Sametime. Value: Any type of data associated with the attribute (text, image, binary).
STExtendAttribute
STExtendAttribute extends the basic attribute with support for heavy and existential attributes:
46
Heavy attribute is an attribute that has a content, but the content was not delivered as part of the attribute. In the case of a heavy attribute where the content is not available as part of the attribute, the application must specifically call on the service supplying the attribute to get its content. This functionality is used in places where attributes may contain large quantities of date (images) that is delivered to a large number of users. In order not to overload the network, the content is delivered only upon specific demand from the application. (See the following method in the toolkit reference guide: WatchList.queryAttributeContent()). Existential attribute is an attribute that does not have any content associated with it. This attribute serves as a marker and is used as a boolean flag indicating the existence or nonexistence of a certain property. Identifying an existential attribute is done via the STExtendedAttribute.isExistential() method. For example, a server attribute indicating whether directory browsing (Directory Service) is supported by the server is marked as an existential attribute. If the attribute exists, then the server supports directory browsing, otherwise not. This concludes our discussion of the Sametime core types.
2.6 Summary
In this chapter we have discussed how Sametime uses an event model, and how the Sametime toolkit offers proxy objects on the client side that the developer can utilize in developing collaborative applications. The Sametime toolkit model contains many interfaces, and we discussed the relationship between events, interfaces, listeners, and adapters. Then we gave an overview of the different services offered by the Sametime client community toolkits. We looked at the basic things you must do in any Sametime program, and finally we explained what the core types in Sametime are. Even though you may never handle any of the core types directly, they are involved in all Sametime functionality.
47
48
Chapter 3.
Places architecture
The Sametime places architecture provides a flexible and robust model that supports place-based awareness and collaboration. In this chapter we examine the basic concepts of this architecture and see how they can be combined to create rich real-time applications. The topics covered are: The Sametime places model with virtual places, sections, activities, and attributes Communication in a place - sending messages or changing attributes The Places API - toolkits Examples of doing things in a place In addition to the place-based samples in this book (in Chapter 6, A place-based auction example on page 101 and Chapter 9, A complex meetings sample on page 207), we encourage you to take a look at the more detailed technical documentation that is provided with the Sametime toolkits as well.
49
50
51
52
persistent places; non-persistent places would be more appropriate for instant meetings (like in Sametime connect client). Although in this context persistent means that you avoid the place being destroyed when there are no more users in it, a persistent place will not survive a server restart.
3.3.2 Sections
In the Sametime places model, a virtual place is made out of sections. This makes the place similar to a natural place that has different rooms inside it. Every user that enters a place enters a specific section in that place (just as, when entering a real place, you actually enter a specific room in that place). This is very useful; for example, an event-hosting application such as an auditorium could separate the audience from the presenters by putting them into different sections. The concept of sections further enhances the awareness and communication capabilities inside a place by adding the notion of scope. Using the same example, the audience can communicate among themselves without having the presenters hear them, and vice versa. Or, one might want to have the audience be aware of the online status of the presenters only, and not of each other. This is possible due to the fact that sections are treated in the places architecture just like any other place member, which means that they can be communicated with in the ways described in the following sections. A special section in the place is entitled the Stage. A user in the stage has more permissions than others. For example, he can talk to the entire place, not only to other users in his section. For the full list of permissions in a place see Table 3-1 on page 55.
3.3.3 Activities
Activities are server-side applications that share the place with the users and define ways in which they can collaborate. Think of activities as servlets for places. Taking the auditorium example again, one might want to add an audio or video activity to the auditorium place to make the presentation more fluid. Activities are treated in the places model just as other place members (users). They can communicate with users and with each other in exactly the same way. As opposed to users, though, activities are very powerful in terms of permissions. One might think of an activity as a super user in the place. For the full list of permissions in a Sametime place see Table 3-1 on page 55. The notion of activities enhances the power inside a virtual place to its maximum. Theres no limit to the things that can be done. From shared Web games to enhanced collaborative meetings, activities are everywhere. In our C++ sample Adding activities to the place on page 281 you can see an example of adding Sametime-supplied activities to a place. In the redbook Working with the Sametime Community Server Toolkit, SG24-6667 we describe how the developer can add a server-side activity to a place with an auction house example.
53
3.3.4 Attributes
All place members have a list of properties describing them, and can also have a list of attributes. Those attributes are in a key/value format and can contain any information that the developer wishes to attach to the given member. While the properties of a member represent static predefined information of a place member and cannot be changed, a members attributes represents dynamic information about it. It should be mentioned here, as well, that attributes of place members should not be confused with other attributes in Sametime like the Online attributes of Sametime entities and the Storage long term attributes. The places attributes exist only in the scope of a particular place and should be treated as such. Refer to the relevant toolkit documentation for further explanations of the online attributes and the storage attributes.
54
listeners on the objects that you are interested in. This model is similar to the standard event dispatching model used by Java technology since JDK1.1 (in the AWT and Swing libraries, for example). The benefits are obvious: it reduces the network traffic and thus enhances the performance of the system.
It should be noted that as for receiving notifications on messages or on attribute changes there are no restrictions. Any member can receive messages that are sent to him either directly or not, and can watch the changes of attributes of any other member. The restrictions only apply to active operations like sending messages and changing attributes of members.
55
56
57
Important: The following are only examples of how you can work with the place architecture. We are showing some sample flows that are common to many programs, but you can do much more with places than is illustrated here. Also, to keep things simple we are not covering any interaction with activities in the following samples.
: PlaceListener
: Place
: PlacesService
We assume you already have a reference to the PlacesService. To enter a place your must have an object that implements the PlaceListener interface.
createPlace
First you create a context for the place you are about to enter. The place may not even exist yet. Among the things you specify in the parameters, besides the place name, is the level of encryption you want to use for communication between your application and the place.
58
addPlaceListener
You need the context for the place before entering it because it allows you to start subscribing to events from the place. Once this has been done you can enter the place.
enter
In the parameters of the enter method you decide whether the place already has to exist, whether you want to create it or whether you dont care. You also select whether you want to enter the place in the stage section or a non-stage section. Remember that somebody else may already have entered the stage and defined a list of allowed users for the stage section. If you are not in that list and you attempt to enter the stage section, you will not succeed in entering the place. Finally, you can also supply a password for the place you are about to enter. If you are the creator if the place you will be setting the password for the place. If the place already exists, you must supply the password that was defined by the creator of the place.
entered
Upon successful entry your PlaceListener object (the one you passed as a parameter to the addPlaceListener method) receives the entered message. Once you receive this you are in the place and can start doing a lot of things. Your PlaceListener object will receive lots of other messages as well, as defined by the PlaceListener interface. For example, you will receive a sectionAdded message for section in the place, an attributeChanged message for all attributes that may exist in the Place, and so on.
59
60
tmp2 = evt.getSection(); if (tmp2.getMemberId().intValue() < tmp1.getMemberId().intValue()) { firstNonStageSection = tmp2; secondNonStageSection = tmp1; } else { firstNonStageSection = tmp1; secondNonStageSection = tmp2; } }
MySectionListener
4 : Section
: Place
addMySectionListener(MySL) getSection( )
Your object must implement the MySectionListener interface, you must have a reference to your place object, and you must have a reference to the section you want to move to. We discussed how to get references to all sections in a place in the previous section.
61
You must use the object that represents you in the place (getMySelfInPlace) and you must add a MySectionListener to change to another section. You may already have a reference to your current section; otherwise, you can use the getSection() method in your MySelfInPlace object. To change the section you then simply send the changeSection message to your MySelfInPlace object and pass the new section object as parameter. The change has been successful if you receive the sectionChanged event. You can get a reference to the new section from the event by using the getSection event method. If you had a event subscription on your old section, you must remove that listener from the old section and subscribe to the new section instead.
: PlaceListener SectionListener
: Section
: Place
getSection( )
Variation B
addSectionListener(SectionListener)
Figure 3-5 Getting your section and listening for section events
62
Your object must implement the SectionListener interface, and you must at least have a reference to your place object. As soon as you have entered a place you can query your place object for your section using the getMySection Place method. Another way to do it is if you have gotten your MySelfInPlace object as described in 3.7.3 on page 61. You can then send it a getSection() message. When you have gotten your section object, you subscribe to section events by adding a SectionListener to it. You then receive events like usersEntered and attributeChanged from your section. You can also add listeners to the other sections in the place in the same manner. If you want to do this you should do it when you receive the sectionAdded Place event (discussed in 3.7.2 on page 60).
: SectionListener UserInPlaceListener
: Section
: Place
addUserInPlaceListener(UIPL) )
attributeChanged(PlaceMemberEvent)
63
Your object must implement the SectionListener interface as well as the UserInPlaceListener interface. You must have a reference to the section you want to get a list of users from. When you add a SectionListener to a section, one of the messages you will receive is usersEntered. You can then get a list of all users by using the GetUsers() method in the event object. If you are interested in a particular user, you can iterate through the list until you find the one you are looking for. If you want to monitor all users, you must add a UserInPlaceListener to each user. Once you have added such a listener you will start to receive events like attributeChanged from the users in the section.
: UserInPlace
: Section
: Place
changeAttribute(STExtendedAttribute) changeAttributeFailed(PlaceMemberEvent)
changeAttribute(STExtendedAttribute) changeAttributeFailed(PlaceMemberEvent)
changeAttribute(STExtendedAttribute) changeAttributeFailed(PlaceMemberEvent)
64
Depending on which object you want to change an attribute for, your object must have an appropriate listener on it. As long as the listener interface you implement inherits from PlaceMemberListener you are safe because it defines the attribute-related events. You must have a reference to the object you want to change the attribute for (if the attribute does not exist it will be created automatically). Then you create an instance of STExtendedAttribute and populate it with a key and a value; and finally you send it off by invoking the changeAttribute method in the Place, Section, or UserInPlace object. If something goes wrong when you attempt to change the attribute, you will receive a changeAttributeFailed message. You can find the error by using the getReason() method in the PlaceMember event object. For example, you will get an error if you are in a non-stage section and attempt to change a place attribute (because you do not have permission to do this). In the next example we look at the event you receive when the change of attribute was successful.
PlaceMember Listener
STExtended Attribute
PlaceMember Event
: UserInPlace
: Section
: Place
getInt( )
65
To receive notification about changed attributes, your object must implement a listener interface that inherits from the PlaceMemberListener interface. We only show this one interface to save a bit of space. As you can see on the figure, your object receives the same type of message no matter whether it is sent from the place, from a section, or from a user. Thus it makes sense to use different listener objects for the different types of place members so you avoid having to spend a lot of code on testing simply to figure out what kind of attribute is changed. However, if it is a user the attribute has changed for, you want to get that user and you do it with the getPlaceMember method on the event object. Before getting the full attribute you may want to simply get the key of the attribute and test whether it is an attribute you are interested in. To get the value of an attribute you must first get the full attribute. Then you can get its value. The STExtendedAttribute class has different methods to get the value depending whether it is a string, an integer, or a binary value. You can also test whether the attribute is existential (contains just the key, no value) or if it is a heavy attribute, as discussed in 2.5.4, Sametime attribute types on page 46.
66
: UserInPlace
: Section
: Place
Even though we show the different interfaces here, your object just has to implement one of the listener interfaces that inherits from the PlaceMemberListener interface to send messages. As you can see in the figure, text or data messages can be sent to other users, to sections, or to the place. If sending the message for some reason does not succeed, your listener object gets the sendFailed message and can extract the reason for the failure from the event object. Remember that there are restrictions on who can send messages where, as we discussed earlier and showed in table format in Table 3-1 on page 55.
67
MyMsgListener
MyselfInPlace
: Place
getMyselfInPlace( ) addMyMsgListener(MyMsgListener)
textReceived(MyselfEvent)
dataReceived(MyselfEvent)
Your object must implement the MyMsgListener and you must have a reference to yourself in the place. You can then add a MyMsgListener to your representation in the place and your listener object will start to receive text and data messages. We also saw the use of MyMsgListener in 3.7.3, Changing your section on page 61 to listen for events when you change to another section. This concludes our discussion of some of the most common operations you will use when implementing an application that utilizes Sametime virtual places.
3.8 Summary
In this chapter we have given you an overview of the Sametime Places architecture. We have explained how a place is divided into sections. Users resides in sections. A place, its sections, and the users in the place can have attributes that other place members can read. Place members can also communicate by sending text or data messages. In the last part we looked at examples of common operations in a place. We discussed the involved objects/interfaces and the event flow.
68
Part 2
Part
Java toolkit
69
70
Chapter 4.
71
72
Also included are other packages to provide support for broadcast clients in Microsoft Internet Explorer or Netscape, as well as Windows platform support for application sharing. Refer to the Sametime 3.0 Java Toolkit Developers Guide for more information about these packages. This document is delivered as a PDF file together with the Sametime 3.0 Java Toolkit. Note: To look at the JavaDoc toolkit documentation, open your browser to the file index.html in the directory where you unpacked the JavaDoc file; in our case the path was: C:\STJava\doc\index.html It is a good idea to bookmark this file in your browser. You can also read this documentation directly from the Sametime server. Look under the Toolkits link. That is it. You now have the Sametime toolkit files installed. What to do next depends on which Java development environment you use. Among the tools we used for our development were VisualAge for Java Enterprise Edition V3.5 and V4.0. However, the Sametime Java API works in any standard development environment that supports JDK 1.1. For those readers who do not have VisualAge for Java or another full-blown Java development environment we first describe how to install and set up the IBM Java Development Kit 1.3. This kit allows you to make modifications to the provided samples, to compile the changed source files, and to verify that they run on your system.
73
Note: If you have VisualAge for Java you do not need to install the IBM JDK 1.3, so you can jump right ahead to 4.3, Setup of IBM VisualAge for Java on page 79.
Select the IBM Developer kit link. At the time of writing you can select between the JDK 1.1.8 and the JDK 1.3 (as part of WebSphere preview technologies). We chose to install the Developer Kit package for IBM JDK 1.3 (although the Sametime Java toolkit supports JDK 1.1, it will run on the 1.3 platform as well). The JDK is free, but you must register and accept a license agreement to get it. Once you download the JDK, install it. During the installation you will be asked if you want to install the runtime environment as the system JVM. We answered yes to this, and used default values for all other options. If you chose to install the runtime environment as the system JVM, you can verify correct runtime installation by running one of the sample programs. We tried to run a sample from the JDK demos with the following command from a command prompt window:
C:\Program Files\IBM\Java13\demo\sound>java -jar JavaSound.jar
However, to compile Java programs (and if you chose not to install the runtime environment as the system JVM) you need to set up the program path and class path on your machine. See the readme.devkit.ibm.html file for instructions on how to do this. It varies slightly depending on which version of Windows you are running. In the following we describe what we did for Windows 2000.
74
In order for Windows to find the required Sametime classes to run and compile Java programs, you must add the toolkit jar file to the classpath variable. We placed our Sametime classes in the following directory: C:\STDev\STJava To set up the path and classpath variables on Windows 2000, do the following: 1. Go to the Start menu. 2. Choose Settings -> Control Panel -> System. Note: Depending on how your system is set up, you may have to open the Control Panel and then double-click the System icon instead. 3. Choose the Advanced tab in the System dialog window. 4. Click the Environment Variables button, then you will get a new window. 5. In the System Variables section find the variable called Path. 6. If you are unable to find the variable, click the New button. If you are able to find the path variable, select it by clicking it, and then click the Edit button. 7. In the variable value section, add a period followed by a semicolon .; then add the full path of the jar file name. For example, if the javac.exe file is located in the default directory, then the following should be included in your path:
.;C:\Program Files\IBM\Java13\bin;
8. Click OK to save your new path. 9. Next, in the System Variables section, find the variable called Classpath; select it and click Edit. 10.In the variable value section, add the full path of the Sametime toolkit jar file names. For example, if the jar files are located in C:\STJava directory, then the following should be added to your classpath:
C:\STDev\STJava\STComm.jar;C:\STDev\STJava\CommRes.jar;C:\STDev\STJava\STMe eting.jar;C:\STDev\STJava\MeetRes.jar;
Depending on what you are developing, you may also have to add some of the other jar files that are included with the Sametime toolkit. 11.Click OK to save your new classpath; click the next OK button to close the dialog for environment variables, and finally, click OK to close the System dialog window. You are now ready to test whether you can compile a Java program with the JDK.
75
3. Then try and run your newly compiled program with this command:
java Groove
If the program runs all right a window will open where you can experiment with the creation of different rhythms. If you did not succeed, go back and verify that you have installed the JDK as described in the file readme.install.ibm.html that is part of the JDK. Also make sure that you have set up your path and classpath statements correctly. We are finally ready to touch some Sametime code.
2. Open the HTML file that is used to launch the sample applet:
76
notepad ChatUIApplet.html
The appletviewer from the JDK that we will use for testing reads necessary parameters from the HTML file. Update the file like this: a. Find the lines with parameters being passed to the applet. Add a line similar to this:
<PARAM NAME='serverName' VALUE='gefion.lotus.com'>
Our Sametime server was named gefion.lotus.com. Replace that name with the name of your Sametime server. b. Change the values for the archive and cabinet parameter so they point to where the Sametime toolkit jar files are stored on your computer. Here are the values we used:
<PARAM NAME='archive' VALUE='../../STJava/CommRes.jar,../../STJava/STComm.jar'> <PARAM NAME='cabinets' VALUE='../../STJava/CommRes.cab,../../STJava/STComm.cab'>
The lines must not wrap in real life. c. Finally, if you do not have a user named Tom with a password of sametime defined on your server, either register that user or update the HTML file to use another loginName and password. d. Save and close the HTML file. 3. Open the Java sample source file with notepad:
notepad ChatUIApplet.java
Now modify the java code to read the server name as a parameter being passed on to it. a. Locate the following line:
String serverName = getCodeBase().getHost().toString();
c. Save and close the Java source file. 4. You are now ready to compile your updated applet. Run this command:
javac ChatUIApplet.java
If you get any error messages, make sure that the classpath is correct. If you did not get any error messages, you can compile Sametime programs on your computer. The next question is whether you can run them. To test applets on our own machine we needed to update the Java security policy.
77
5. To update the Java security policy for your machine change to the following directory:
C:\Program Files\IBM\Java13\jre\lib\security
In this file we can control security in a granular fashion. However, to make things easier for testing we will allow everything. 7. Insert the following line in the grant section of the file:
permission java.security.AllPermission;
Save and close the file. Note: This change to the java.policy file disables all Java security mechanisms. Do not use this on any computer that runs application in production. Use it only for development purposes. We are about ready to test the new applet. 8. Change back to the sample directory:
C:\STDev\STJavaSamples\ChatUIApplet
You should now see a window that allows you to type in the name of a person you want to chat with and then an invitation to that person. If you did not get this result, retrace the steps in this chapter until you find out what went wrong.
78
Note: If your application consists of more than one class, it makes sense to gather all your classes in zipped format in a jar file that can be downloaded to the Web browser by one transfer. You use the JAR program to create a jar file. Open a command line; enter JAR and press Enter and you will be shown help for the JAR command. For example, to create a jar file named myapp.jar that contains all files in the current directory, you can use the command:
jar -cvf myapp.jar *.*
If your jar file contains a Java application (and not an applet) you may have to edit a file called the manifest file for the jar file to specify which class is the main class. You can read more about jar and manifest files in the documentation that comes with the JDK. This concludes our simple compile and run verification test. The next section is about how to set up VisualAge for Java for Sametime development. Note: If you want to use the JDK or another Java development environment other than VisualAge for Java, you can skip the remainder of this chapter and turn to Chapter 5, Introduction to Sametime Java applets on page 89.
79
1. Open VisualAge for Java and go to the Workbench window. In the Workbench window, with your mouse, right-click to receive a popup menu and select Add -> Project. 2. Select Create a new project named: and enter the name of the new project that will receive the Sametime API classes. We used Sametime Comm Toolkit. Click Finish to create the new project. 3. In the Workbench window you should see the newly created project. Select the Sametime Comm Toolkit project. Right-click to get the popup menu and select Import. 4. You will be presented with a dialog where you can specify how to import the Java code. Select Jar file and click Next. 5. You should now see a dialog where you can specify the location of the jar file you want to import. Click the Browse button and maneuver to the directory that you extracted the Sametime 3.0 Java API into; in our case it was C:\STDev\STJava. Select STComm.jar and click Open. 6. Check the class check box. You can try to click the Details button associated with the class check box. This displays a dialog where you can see which class files will be imported. All files should be selected, but to make sure click the Select all button to select all the classes within this jar. When done, click the OK button to return to the previous screen. 7. Make sure that the Project text box contains the name of the project we will be importing the jar files classes into. For us this project is Sametime Comm Toolkit. If the project name does not exist in the text box, click the Browse button and select the project to import the classes into. 8. Click Finish to commence the import process. You will be presented with a status bar indicating the progress of the import. 9. Repeat the previous steps for the CommRes.jar file located in the directory we extracted the Sametime Java API into. This file only contains resources, so while the class check box can be selected the important thing is that the resources check box is selected. Once you have imported all the classes and resources into your project, your VisualAge for Java Workbench should look something like that shown in Figure 4-1 on page 81.
80
81
the directory that you extracted the Sametime 3.0 Java API into; in our case it was:
C:\STDev\STJavaSamples\CustomizeChatUI\
Click OK to return to the Import dialog. 4. The samples come as source code, so instead of the class check box we select the java check box as well as the resources check box. If you click the Details button associated with the Java check box you can see that three java files are targeted for import. When done, click the OK button to return to the previous screen. 5. Make sure that the Project text box contains the name of the project you will be importing the jar files classes into. For us this project was Sametime samples. If the project name does not exist in the text box, click the Browse button and select the project to import the classes into. 6. Click Finish to commence the import process. You will be presented with a status bar indicating the progress of the import. The imported applet will be placed in the default package for the project. You can change this if you want, but the default location is fine for our purposes. Before you can give the Sametime applet a test run there are a few additional things you have to do. To test a basic applet in VisualAge you can right-click on the project for the applet and select Run->In Applet Viewer from the popup menu. You should now be able to view your applet running within VisualAge for Java. However, if some code that your applet uses is in a project other than the one currently selected, you need to make sure that the classpath has been set correctly. For Sametime applets you also need to make sure the Java security settings on your computer allow testing them since they will access another machine. We discuss this in the following sections.
82
To accomplish this, follow these steps: 1. In the Workbench, select the applet that you wish to execute (in our case CustomizeChatUI) and right-click your mouse to obtain the popup menu. Select Properties and a dialog window opens. If it is not already selected, click the Class Path tab and you will be presented with a dialog similar to Figure 4-2 (except that we have not specified project path yet).
2. Ensure that the Include '.' (dot) in class path check box is checked. 3. Check the Project path check box and click the Edit button. You will be presented with a dialog with a list of all projects in VisualAge. 4. Select all the relevant projects that you require to be in the classpath for this particular project. For our example, we need the Sametime Comm Toolkit project. Select the project and click OK. 5. Click the Compute Now button to make sure your changes are reflected in the current environment. A warning will be shown. Accept to compute the new class path. 6. Finally, click the OK button to return to the Workbench window. The classpath is now set up correctly.
83
Our Sametime server was named gefion.lotus.com. Note: The parameter serverName is not included in the original sample. The code assumes that the Sametime server is the same as the applet is launched from, which is not the case for us. We could hard code the server name in the code while we test, but using a parameter makes this a bit more flexible. 3. Click OK to save your new properties. We will now modify the Java code to read the server name as a parameter being passed on to it. 4. Find the init method for the CustomizeChatUI class: a. Locate the following line:
String serverName = getCodeBase().getHost().toString();
c. Save your changes. Now we only need one more thing before we can test the applet.
84
2. Open the java.policy file using notepad or your favorite text editor and add the following policy in the big grant section:
permission java.security.AllPermission;
When complete, the beginning of your changed java.policy file should look similar to Figure 4-3.
// Standard extensions get all permissions by default grant codeBase "file:${java.home}/lib/ext/*" { permission java.security.AllPermission; }; // default permissions granted to all domains grant { permission java.security.AllPermission;
Figure 4-3 Part of the java.policy file with a new line added
Note: This change to the java.policy file disables all Java security mechanisms. Do not use this on any computer that runs applications in production. Use it only for development purposes. 3. Save the java.policy file.
85
You should now have enough basic knowledge to start working with Sametime development in VisualAge for Java.
2. Right-click your mouse to obtain the popup menu. Select Export... The Export SmartGuide window opens. 3. Select to export a Jar file and click Next. 4. Specify a file name for the jar file, for example:
custui.jar
86
Make sure there is a check mark for .class and resource. It is optional whether you want to export the Java source as well. 5. Click the Details... button for resource. A new dialog named Resource Export opens. 6. Select the Sametime samples project in the left list box and make sure that logo.gif is selected in the right list box. Click OK to return to the SmartGuide window. 7. You can select to have an HTML file to launch the applet generated automatically by VisualAge. To do this set a check mark next to .html. The parameters we entered for the applet class in VisualAge are used to create the HTML file. 8. Finally, start the export by clicking the Finish button. The compiled classes and the logo.gif file are exported to the custui.jar file and the HTML file is created in the same directory. That is it. You can move your jar and HTML files to a directory under the Sametime server. You may have to edit the archive statement in the HTML file to include the Sametime toolkit jar files (STComm, CommRes.jar, STMeeting.jar, MeetRes.jar, and so on). Once you have done this you are ready to test your applet in the real world!
4.4 Summary
In this chapter we have discussed how you get the Sametime Java client toolkit, and how you set it up, develop, and run programs using the IBM JDK 1.3. We have also gone through the same exercise for VisualAge for Java.
87
88
Chapter 5.
89
90
import java.applet.*; /** * Sample Sametime Applet<p> * Introduces basic Sametime functionality */ public class QuickStart extends Applet implements LoginListener { protected STSession m_session; protected CommunityService m_communityService; }
5.1.2 init()
The init() method is called by the browser when an applet is prepared, but not yet shown to the user. The applet can prepare itself now. The code for the init method is in Example 5-2 on page 92. All Sametime applications need a session. It provides a central place to register and locate component/services on the client application. Since this object needs to be created only once, we do this in the init() method shown in Example 5-2. The session object is created with a unique String identifying it. Then all components are loaded: here you can optimize by loading only the required components and thus reduce the toolkit size. Among the components loaded by the loadAllComponents method is one called STBase. This component is the one that is responsible for communication with the Sametime server, which obviously makes it a required component for all Sametime applications. Finally, the session is started. Note about the Java Toolkit: When the services objects are created and activated, they are actually dynamically loaded from the codebase. This means that you can optimize your application download size by providing only the classes needed. The loadAllComponents method you see in Example 5-2 will simply skip components that it cannot find, so you do not need to change your application even though you have repacked the Sametime toolkit jar(s) downloaded together with your applet.
91
5.1.3 start()
The start() method is called when the applet is becoming active, for instance, visible to the user. After a session is created, it will be used to create certain Service objects. Services can be proxy objects to server-side implemented services, or just utilities which act on the client side only1. The first service we have to create is the CommunityService. It provides basic functions like authentication. If your application needs other services, we suggest that you just add the service creation to the start() method of your applet. See 2.3, Sametime services: What can I do with them on page 29 for a description of Sametime services. Until now, the application has not yet talked to the server, and no network connection has been created, so the session object and the community service object are not very useful. Lets log into the server and get ready to receive events. Before logging in, we need to register for the login events.
It is not important to know this for the function of the application, but it may be important to know when you optimize your application and want to know if events traverse the network or not.
92
5.1.4 stop()
The stop() method is called by the browser when the applet is becoming inactive. We use this to log out of the Sametime server.
Example 5-4 QuickStart stop() method
/** * Applet stopped. <p> * Logout */ public void stop() { if (m_communityService != null) m_communityService.logout(); }
5.1.5 destroy()
The destroy() method is called when the applet is destroyed and has to free all resources. We close and unload the session object.
Example 5-5 QuickStart destroy() method
/** * Applet destroyed.<p> * Stop and unload the session.
93
94
If you try and run the supplied applet in a browser, you will not get much excitement out of it since the applet does not have any user interface. However, you can still verify that the applet actually works by opening the Java console for your browser. Once the applet has logged successfully into Sametime, it writes this information to the console, as you can see in Figure 5-1.
Figure 5-1 QuickStart applet writing to the Java console in the browser
In the following section we discuss how to extend our applet to have the user enter a Sametime place.
95
We implemented start() and stop() again to reflect the new service used, as shown in Example 5-8.
Example 5-8 PlaceQuickStart applet methods
/** * Applet started.<p> * Create the places service */ public void start() { m_placesService = (PlacesService) m_session.getCompApi(PlacesService.COMP_NAME); super.start(); } /** * Applet stopped. <p> * Logout */ public void stop() { if (m_place != null) m_place.leave(0); super.stop(); }
96
public void loggedIn(com.lotus.sametime.community.LoginEvent event) { super.loggedIn(event); m_place = m_placesService.createPlace( "SamplePlace", "Sample Place", EncLevel.ENC_LEVEL_DONT_CARE, 0); m_place.addPlaceListener(this); m_place.enter(); }
The first new interface PlaceListener brings a lot of methods to be implemented. We implemented all these methods to write to the system console. In Example 5-10 you see only entered() as this will be important for any application to listen for. We add the applet as a MyMsgListener to the place to inform us about any place-related messages. As you will see on the console, there will be other events arriving when a place is entered. Sametime informs the application about the place entered automatically. To keep things simple we use our applet class as the listener, but as your application grows you may consider outsourcing the listener responsibility to an independent adapter class, as we discussed in 2.2.4, Adapter on page 26.
Example 5-10 PlaceQuickStart: entered()
/** * entered<p> * Listen for incoming messages<p> * Send a message to the place */ public void entered(PlaceEvent event) { System.out.println("entered"); m_mySelf = m_place.getMyselfInPlace(); // Listen for incoming messages. m_mySelf.addMyMsgListener(this); m_place.sendText("Hello, place!"); }
97
The second new interface is the MyMsgListener interface which will be called when messages (text or binary data) arrive. We just show the message on the transcript.
Example 5-11 PlaceQuickStart: textReceived()
/** * textReceived<p> * We received text from a place member */ public void textReceived(com.lotus.sametime.places.MyselfEvent event) { System.out.println("textReceived: " + event.getText()); }
The code in this chapter is a bare-bones place application example. It will not give you much joy on its own, but if you use it together with the Auctioneer client program we develop in the next chapter, you should be able to see the code working by utilizing the browsers Java console again.
Then, in the listener method for the failed event, we can send the failure code through a switch where we test it against the possible error for that type of error message. In Example 5-12 you can see an example of how to figure out why entering a place fails.
Example 5-12 Testing why entering a place failed
public void enterFailed(PlaceEvent event) { switch (event.getReason()) { case STError.ST_ERROR_PLC_ACTIVITY_ALREADY_EXIST: System.out.println("ST_ERROR_PLC_ACTIVITY_ALREADY_EXIST "); break; case STError.ST_ERROR_PLC_ACTIVITY_NOT_EXIST: System.out.println("ST_ERROR_PLC_ACTIVITY_NOT_EXIST "); break;
98
case STError.ST_ERROR_PLC_ALREADY_EXISTS: System.out.println("ST_ERROR_PLC_ALREADY_EXISTS "); break; case STError.ST_ERROR_PLC_ATTR_NOT_EXIST: System.out.println("ST_ERROR_PLC_ATTR_NOT_EXIST "); break; case STError.ST_ERROR_PLC_DELETED: System.out.println("ST_ERROR_PLC_DELETED "); break; case STError.ST_ERROR_PLC_FULL: System.out.println("ST_ERROR_PLC_FULL"); break; case STError.ST_ERROR_PLC_INVALID_PASSWORD: System.out.println("ST_ERROR_PLC_INVALID_PASSWORD "); break; case STError.ST_ERROR_PLC_MEMBER_NOT_EXIST: System.out.println("ST_ERROR_PLC_MEMBER_NOT_EXIST "); break; case STError.ST_ERROR_PLC_NOT_AUTHORIZED: System.out.println("ST_ERROR_PLC_NOT_AUTHORIZED "); break; case STError.ST_ERROR_PLC_NOT_EXIST: System.out.println("ST_ERROR_PLC_NOT_EXIST "); break; case STError.ST_ERROR_PLC_SECTION_NOT_EXIST: System.out.println("ST_ERROR_PLC_SECTION_NOT_EXIST "); break; case STError.ST_ERROR_PLC_STAGE_NOT_EXIST: System.out.println("ST_ERROR_PLC_STAGE_NOT_EXIST "); break; case STError.ST_ERROR_PLC_TYPE_ERROR: System.out.println("ST_ERROR_PLC_TYPE_ERROR "); break; case STError.ST_ERROR_PLC_USER_ALREADY_PARTICIPATE: System.out.println("ST_ERROR_PLC_USER_ALREADY_PARTICIPATE "); break; default: System.out.println( "\n ************Failed to enter/create the place. Reason: " + Integer.toHexString(event.getReason()) + "h ************ \n\n"); break; } }
99
Sometimes the Sametime toolkit documentation lists possible error codes associated with an event. In other situations you may look at the STError constants and determine which are relevant for the errors you want to catch. The two applets we described in this chapter are available as source code from the IBM Redbooks Web site. See Appendix E, Additional Web material on page 407 for instruction on how to get the source. To keep things simple we have just implemented one class for the QuickStart applet and another class that extends QuickStart for our PlaceQuickStart applet. However, we also include a second version called PlaceQuickStart2 where we illustrate the use of an inner class to act as PlaceEventsListener by extending the toolkit PlaceAdapter class. We discuss the advantages of using adapters in 2.2.4, Adapter on page 26 and inner classes in Using nested classes in Java as listeners on page 27. By extending a PlaceAdapter and a MyMsgAdapter in our simple case we were able to remove 18 do-nothing methods from our PlaceQuickStart class.
5.3 Summary
In this chapter we have walked through the code used to implement a basic Sametime applet. Next we extended the sample by adding the capability for our applet to enter a place and listen for messages in that place. We gave this introduction because the big sample in the next chapter is implemented with two different Sametime client applets.
100
Chapter 6.
101
Note: We want to show usage of the Sametime community services in detail, concentrating on places. So, for instance, we do not discuss the Sametime UI components very much in this chapter. However, the Sametime Java toolkit comes with a series of well documented examples for the UI components.
6.1 Overview
Sametime virtual places are generally well suited for situations where several geographically dispersed people need to collaborate in a structured manner in real time. While we discuss a specific application in this sample, much of the implementation has general relevance. For our application we want to enable people to bid on items on line in real time. We model our solution after a traditional auction that is performed over a short period of time. The auction runs until nobody places a higher bid and the auctioneer calls 1 - 2 - sold! This is in contrast to the auctions from eBay and similar companies that model their solutions after silent auctions that run over a longer fixed period, where the winner is whoever has placed the highest bid during that period. In Figure 6-1 on page 103 you can see a basic illustration of the real life auction we want to model our virtual on-line bidding after.
102
On-lookers
We have an auction that consists of the following: An auction item: A thing that somebody has put up for sale. In our scenario we concentrate on what happens after the item has been commissioned to the auction house, so we do not cover any interaction with the seller. Bidders: People who want to buy the auction item. Auctioneer: The person responsible for running the auction. The auctioneer announces what is up for sale, sees who places a bid, relays the bid to all bidders and on-looker; and finally calls 1 - 2 - sold when the highest bid has been placed. On-lookers: People who are interested in following the auction, but who are not allowed to participate as bidders. The on-lookers may join the bidders group by going through some process like registration, credit check, or the like.
103
We want to hold similar virtual auctions where people participating can be anywhere in the worlds, as long as they have network connectivity (and a Web browser in our first iteration). We can deduce a few of our requirements by looking at Figure 6-2.
On-lookers
The people participating in the auction need different rights and capabilities depending on which group (or section) they belong to. The auctioneer must be able to: See all participants Place auction items where all participants can see them. Hear all bidders Be heard by all participants Finish or stop the auction The bidders must be able to: Bid on an item See the other bidders and engage in private conversations with any of them
104
The bidders must not be able to: Broadcast messages to all participants. A bid must be passed on to the auctioneer, who then relays it to all participants. The on-lookers must be able to: Watch the bidding Join the bidders group by going through some process The on-lookers must not be able to: Place a bid All participants must be able to see the details about the auction item. There are obviously many more requirements for such an application, but we will stick with the basic ones we have listed here, and see how we can use the Sametime Places service as the underlying framework for our solution. As explained in Chapter 3, Places architecture on page 49, a Sametime virtual place contains sections. When using the Java client toolkit, a virtual place has three sections. One of the sections is called stage, and users in that section have more capabilities than users in the other two other sections. Looking back at Figure 6-2 we see that our requirements fit well with the capabilities of the Sametime virtual place. We can place our participants in three different sections that fit the three different groups of people we have identified in the figure. The first section is the stage section, where the auctioneer resides and acts from. It is access controlled, so that only the auctioneer can enter it. (Imagine a security guard protecting the stage and the auctioneer.) Remember that the stage section gives the user some additional rights, which are discussed later in this chapter. The second section is the anonymous section, where anonymous users are staying. An anonymous user is a user who logs into sametime without a password, and who can only watch the auction, but who cannot participate by bidding. The third section is the auditorium section, where the authenticated users are staying and have the option to participate in the auction by sending bids. With this design, users can enter the place after having been authenticated against the directory on the Sametime server or an LDAP directory. However, users can also enter the place anonymously. We use this to distinguish between the bidder group and the on-lookers by placing them in different sections. People who have an entry in the directory and are able to log in will be allowed to bid,
105
while people who enter anonymously only are allowed to watch the auction. We will allow people in the same section to see each other and initiate one-on-one conversions via instant messaging. The auctioneer is allowed to see all participants and to initiate instant messaging sessions with anybody. Finally, there is the auction item. It does not enter the place as a user. Instead, we let the auctioneer bring it into the place and then make the auction participants aware of it through place attributes. We now discuss some of the main objects and some of the flow in the application.
106
Auction item title Action item URL (for location of picture file) Action item minimum price In addition the place has a property for the current state of an auction: AuctionStatus - InActive, Active, One, Two, Sold The place also contains important objects of the following class that we have not mentioned so far: Section - When a user enters a place, they also have to enter a section. Using the Sametime Java client toolkit, we have access to three sections in our place, and we put our users in different sections depending on who they are as we discussed a bit earlier. There are obviously more objects in our implementation. For example, we use an abstract class because the Auctioneer and the AuctionCustomer have much in common. We use listener objects to receive events from the place and we need some user interface objects, but the objects we have listed here are the most important ones from the business point of view.
107
Before setting the auction status to active the auctioneer starts to listen for text and data messages from other place members. The auctioneer also locks the door to the stage section, so only users that the auctioneer specifies are allowed to enter the stage section. In our sample only the auctioneer is allowed to enter the stage section. Once this setup is done, the auctioneer starts the auction by setting the auction status place attribute to ACTIVE.
108
6.3.5 Calling
The auctioneer has three buttons, with the labels One, Two, and Sold. When it is time to call the auction, the auctioneer clicks button One. This results in the auction status attribute of the place being set to ONE and a message about this calling being sent to all place members. As we saw in the previous section, a new bid will reset the status of the auction to ACTIVE. If no further bids arrive, the auctioneer clicks button Two, and following that, the Sold button. The same changes and notifications as for the first call are performed in each case. When the auction status attribute for the place is set to SOLD, nobody can place additional bids. Keep these flows in mind as we move on and look at the implementation.
109
Auctioneer client is the user interface and dashboard for the auctioneer. AuctionCustomer client allows customers to participate in auctions, or simply watch if they have entered anonymously.
We implemented these clients as Java applets embedded into simple, static .HTML pages. However, you can as easily embed them into a JavaServer page (JSP) or another dynamic environment. In our design we strived for the sample to be flexible and extendable, so if you want, you can implement an HTML-only version for use with a PDA or a cellular phone that supports WML. For example, if using the Java client toolkit, the AuctionCustomer HTML client can be implemented by a servlet that handles the Sametime communication. See Figure 6-3 on page 111 for a functional diagram of the place that both of our clients access. You can see it maps very well to our analysis of the situation in Figure 6-2 on page 104.
110
Place attributes
Current Item
Current Bid
Auction Status
Auditorium section
Stage section
The arrows show how messages or attribute modifications are happening. This figure does not show the listeners; they are described in detail later.
Then the auctioneer enters an application, as shown in Figure 6-4 on page 112.
111
We can group the user interface into these components: In the top left corner is the auction status area the current item, its details and the current mode of the auction. Below the status area are the auction controls: two buttons, labeled Start and One. The label on the Start button can be changed to Stop, and button One (first call) can be changed Two (second call) and Sold. In the top right corner are 2 place awareness lists for two sections, used for monitoring the users in the associated sections. At the bottom is a transcript area with a text entry field and a Send button When the auctioneer wants to start the auction, he presses the Start button and the auction is running. Now the customers can start bidding, while the auctioneer can send messages to all users (using the text field) or to individual users by clicking their names in the awareness lists. While the auction runs, the auctioneer also can start to complete the auction by clicking on the button labeled One. He also can always stop the auction by pressing the Stop button for any important reason. In case no new bids arrive, the auctioneer presses the Sold button and the item is sold to the user who entered the last bid. The auctioneer can move on to the next item.
112
If the user enters a name and clicks Anonymous, he will be logged in as an anonymous user, enter the anonymous section, be able to watch the auction only, and can log in at any desired point in time. If the user enters a user name and a valid password, he will log into Sametime as a normal user, enter the auditorium, and be able to participate in the auction. The customer application is shown in Figure 6-5.
As you see, its layout is similar to the auctioneers. The main differences are: The auction controls are replaced by bidding controls. The availability of the Logout button, which can switch Login.
113
The customer has only one place awareness list, showing him the users in the his section. The customer cannot send messages to all users at once, like the auctioneer can. If the customer entered as an anonymous user, he can click the Logout button, then log in again, with name and password, to participate as a normal customer.
Core package
The core package (or parts of it) is always a prerequisite for doing any Sametime programming. We use the following parts:
Core types
This includes classes like STUser, STUserInstance, and STAttribute. See 2.5, Core types on page 42 for more information about core types.
Constants
The core constants contain declarations of constants we use for the level of encryption for the place, and error codes. The package also contains constants used for instant messaging and meetings.
Comparch
The Comparch package defines the core classes STSession, STEvent, and SessionTable, as well as the abstract interface STCompApi that virtually all services in the toolkit extends.
114
Community service
The community package is the next prerequisite for doing anything in Sametime. Its main class is STBase, which implements the community service. We use the community service to log into the Sametime community by password, by token, or anonymously.
Places service
The places package is the one we utilize most on our application. It contains the necessary classes and interfaces for the places architecture, as described in Chapter 3, Places architecture on page 49.
115
Even though we have two applications, we only show one hierarchy because the Auctioneer and the AuctionCustomer share many classes. The classes used by only one of the applications are: Auctioneer AuctionItem AuctionManagerPanel AuctionCustomer CustomerBidPanel CustomerLoginDialog We generated the hierarchy view in VisualAge for Java (by selecting the package for our application and choosing Selected -> Open To -> 1 Classes). This is why there are some small symbols in the figure. The small square after some of the classes that looks like a document means that we do not have the source for that class. The letter A after a class means that this is an abstract class. Finally, the running figure after a class means that class is executable.
116
We discuss some of the classes individually a little bit further on; here we explain which classes map to the real world objects we listed in 6.2, Objects in the application on page 106.
117
MyMsgListener We need to add a MyMsgListener to be able to receive messages from other users. We can receive either text or data messages. These listener interfaces only have a few messages defined, so we do not have to add a lot of methods to our classes to utilize them. Another example where we implement a listener interface directly in our class is in AuctionCustomer: MySectionListener We use this when we need to switch to the auditorium section for authenticated users. The MySectionListerner either sends a sectionChanged or a changeSectionFailed message. Other listener interfaces in the Sametime toolkit define many messages. In these cases it makes sense to move the handling of these messages out in a separate object. In our case, the following listener interfaces define a lot of messages: PlaceListener The PlaceListener itself defines 11 messages and inherits six additional messages. We use it to figure out whether our user entered the place successfully, get notification when attributes change, get a handle to the sections in the place, and so on. SectionListener The SectionListener only defines four messages, but also inherits another six messages. We use it to figure out which users already are in the different sections. A class that implements a listener interface must also have methods for all the messages the interface defines. This is required even though we only are interested in a few of the messages. Thus there is good reason to implement the listener methods in a separate class: so our main class does not get filled up with a lot of method stubs. To make the implementation of such listener objects easier, the Sametime toolkit supplies Adapter classes. They are classes that implement a Listener interface and contain stub methods for all messages defined by the interface. We can then subclass such an adapter class, and only implement the methods for the messages we want to handle. We have three such adapter subclasses in our application:
118
APPlaceAdapter The APPlaceAdapter extends the PlaceAdapter class, which implements the PlaceListener interface. We use APPlaceAdapters for listening to place events for our Auctioneer and AuctionCustomer clients. In our implementation we chose to handle six of the 17 messages defined. We do not do any fancy processing, but simply forward the event to our main class (Auctioneer or AuctionCustomer) that set up the adapter. APSectionAdapter This class extends the SectionAdapter, which implements the SectionListener interface. The only event we listen for is the usersEntered message. In this case we also simply forward the event to the class (Auctioneer or AuctionCustomer) that set up the adapter. APUIPlaceAdapter In our interim implementations we let the main class receive notification when place attributes changed and then passed that information on to the user interface classes. However, to have as little UI code as possible in our main classes, we decided to let the UI classes listen directly to the place instead of using the Auctioneer or AuctionCustomer classes as relays. We created APUIPlaceAdapter, which extends the PlaceAdapter, for this purpose. For the UI we only want to know when attributes in the place change, so the only message we have implemented a method for in the adapter is attributeChanged.
119
Auctioneer
TextArea m_taTranscript
TextField m_tfSend
Button m_btnSend
Starting in the middle left side and moving clock-wise around the diagram, the classes are described as follows: AuctionManagerPanel This panel extends APPanel and implements the Start/Stop and the Call (One, Two, Sold) buttons. It uses APUIPlaceAdapter to listen for changes in the status of the auction and updates the labels of the Start/Stop and the Call buttons accordingly. This class also initializes the following class: AuctionStatusPanel AuctionStatusPanel class extends APPanel and is responsible for displaying information about the current auction item, as well as the auction status (active/inactive). It uses APUIPlaceAdapter to listen for changes in the auction item attributes, like latest bid. PlaceAwarenessList This class is part of the Sametime Java toolkit. It displays all users in a specific section in a place. Thus the Auctioneer uses two lists to be able to see all bidders (in the auditorium section) and all on-lookers (in the anonymous section).
120
As you see in Figure 6-8, the UI of the AuctionCustomer is very similar to that of the Auctioneer.
AuctionCustomer
CustomerBidPanel m_pnlBidPanel
TextArea m_taTranscript
Only one PlaceAwarenessList is used (so users only can see those in the same section as they are in). The other difference is that the AuctionManagerPanel is replace by: CustomerBidPanel This panel extends APPanel. It is responsible for supplying the entry field and Bid button so the user can place a bid. These controls are disabled if the user is anonymous. The panel also supplies a Logout/Login button to allow anonymous users to log out and then log in again as an authenticated user that can bid on the auction items. It uses the APUIPlaceAdapter to listen for the auction status attribute. If the status is INACTIVE or SOLD, the bidding controls are disabled. All other status messages will result in the controls being enabled. The remaining UI classes are simple helper objects: ImageCanvas This class is a simple Canvas, which shows any picture. It is used to display the picture of the current auction item, whose location is passed along to us as a place attribute.
121
LoginDialog and CustomerLoginDialog These are simple dialogs for authentication. The CustomerLoginDialog allows the user to select authenticated login or anonymous entry.
122
The .HTML file for the AuctionCustomer is even simpler: see Example 6-2. The DefaultImageURL is used here also, to show an image until the place attribute is received.
Example 6-2 HTML for AuctionCustomer applet
<HTML> <HEAD> <TITLE>AuctionCustomer</TITLE> </HEAD> <BODY BGCOLOR="C0C0C0"> <APPLET CODE=com.ibm.redbooks.sg24_6666.AuctionCustomer.class WIDTH=100% HEIGHT=100%> <param name=archive value="6666auction.jar,CommRes.jar,STComm.jar"> <param name=DefaultImageURL value="default.jpg"> </APPLET> </BODY> </HTML>
In Appendix B, Working with the auction house sample material on page 375, we describe how to install our sample if you want to try it out yourself. We now move on to look at the dynamics in our application.
123
In Table 6-1 you see the event flow for the startup phase, while both the Auctioneer and all the AuctionCustomers prepare for the auction. There is no interaction yet.
Table 6-1 Startup phase
Event init() Description Called when the applet was loaded in the browser. Called when the browser wants the applet to start. Called by the Community Service if the login was successful. Called by the Places Service if the place was entered successfully. Auctioneer actions Read the item information from applet parameters. Initialize the UI. Try to login. Create the place object, Create the place adapter, Register for place events, Try to enter the place Listen to the stage section. Listen for MySelf events. Initialize auction status. Activate first item. Limit access to stage. Enable UI. Listen to my section. Listen for MySelf events. Bind the PlaceAwarenessList. Enable UI. AuctionCustomer actions Initialize the UI.
start() loggedIn()
entered()
attributeChanged()
Called by the place initially after entering the place for each relevant attribute.
124
Event sectionAdded()
Description Called by the place Service after entering the place for each section in the place.
Auctioneer actions Listen to this section. Identify stage, anon and auditorium section. Bind awareness lists to section. -/-
AuctionCustomer actions Listen to this section. Identify stage, anon and auditorium section. If login was not anonymous try to change section. Listen to the new section. Bind place awareness list to new section.
sectionChanged()
userEntered()
Now, when both actors have entered the auction place, the interactions begin. Since the auctioneer controls the session, the customer cannot do anything until the auction is started. A typical auction runs as follows: The auctioneer changes the auction status to active by changing a place attribute. The customers start bidding by sending data to the auctioneer. The auctioneer automatically accepts/rejects the bids. The auctioneer calls 1-2-Sold, again by changing the place attribute. The auctioneer stops the auction. See Table 6-2 for a general description of the used events.
Table 6-2 Auction phase
Event textReceived() Description Another place member has sent text. Auctioneer actions AuctionCustomer actions
Add to the transcript area. Note: In our sample only the Auctioneer UI has the ability to send text messages.
125
Event dataReceived()
Auctioneer actions Check the bid is sent by an authenticated user from the auditorium section. Compare the bid to the current bid, either accept the bid and notify sender and all users, or reject the bid and inform sender only.
attributeChanged()
Called by the place when an attribute (for example the bid attribute) has been updated.
The shutdown phase is entered when a user closes his window. If an AuctionCustomer logs out or logs in while the auction is performed, the application runs through parts of the shutdown phase (leave()), followed by parts of the startup phase. See Table 6-3 for a description of the related events.
Table 6-3 Shutdown phase
Event stop() left() Description Called when applet is stopped. Called when we left the place. Auctioneer actions AuctionCustomer actions
Close the login dialog if needed, remove the UI, leave the place. Remove listener on section and place. Close the place. Log out. If the reason was a wrong password then try to log in again. Remove listener on section and place. Log out. -/-
loggedOut()
Called when logout() was processed or for other reasons. Called when the applet has to free all resources.
destroy()
If you want to get an idea of how some of this flow looks in our final sample, look at Appendix C, Sample auction scenario on page 381. There we take you through an auction scenario and show screens for each step.
126
We now discuss how you can leverage the places architecture in your own applications. In the following sections youll learn about the functions and restrictions the places architecture offers you. We describe possible actions (for instance, sending text messages), depending on the environment (sender, receiver, location). As a sender we only describe the users view, since this is the only object you can program with the client toolkit.
127
Note: We look at the server-side capabilities in places in the companion redbook Working with the Sametime Community Server Toolkit, SG24-6667.
The receiver can be any place member (user, section, activity, place). The location is the section your user has entered. Sametime distinguishes between the stage section and the other sections. In applications developed with the 2.5 Toolkit there are always one stage section and two other sections. We also give some ideas on where to use the described features, and finally, how we used it in the auction house sample.
section only
As you can see in the table, the user on stage has more options to send text and data to other place members. For instance, by allowing only some users on the stage, you can ensure that place messages are always coming from a trusted user.
128
In the auction house sample we allow only the auctioneer on the stage (by having the auctioneer user set an access list for the stage that only contains the user itself), and when the auctioneer notifies all users the messages are sent to the place. No other client can send messages to the place (and disturb the bidding) because they cannot enter the stage section.
As you can see in the table, users can change their own attributes and their sections attributes. Users on the stage can change place attributes, while other users cant. Again, you can use these limitations to establish some trust in your application. Since the stage access can be limited, a place attribute can be trusted more than a section attribute. Another view could be that users notify other users in the same section about status changes using attributes.
129
In the auction house sample we use places attributes set by the auctioneer to reflect the status of the auction.
For most events, your application has to register an object as a listener. For instance, if you want to listen to place events, you need to register an object implementing the PlaceListener interface. Subscribing does not mean that all possible events are forwarded: Sametime does some filtering. For instance, if your application listens to place events, the entered() method is called only if your applications object successfully entered the place. It will not be called when other users enter the place.
Automatic subscription changes
The mySelfListener() interface methods are called with relevant messages automatically. So your application has to subscribe for MyselfEvents only once, and all relevant events are sent using the dataReceived() or textReceived() methods. For instance, if another section is entered, automatically all messages sent to the new section are forwarded using these methods, while the previous sections messages are not arriving any more. We also want to note here that the object sending events also receives this event. In case of an error, only the sender will receive a failure event instead. Table 6-6 on page 131 gives an overview of under which circumstances a user in a place receives a message, or how to get notification about a changed attribute, a place/section entered, or activity added/removed event.
130
Activity added/removed
In the auction house sample application we register only the needed listeners, and receive only the messages we need to perform the auction. If you look at the code, you may wonder why the method attributeChanged in the AuctionCustomer class does not do anything. How does the bidder know when a new bid has arrived? The explanation is that the user interface class (named AuctionStatusPanel) for the bidder also has registered a listener (named APUIPlaceAdapter) with the place. Thus the view gets the required data to display directly from the source. The bidder sees the higher bid, and can then manually place a higher bid. Consider that a bidder might want to bid automatically on an item up to a certain amount. In this case it makes sense to handle this in the AuctionCustomer class. We have added sample code in the additional Web material to illustrate a very basic example of such auto bidding. To try it out you just need to remove two lines that are commented out in the constructor method or the CustomerBidPanel class. This will add an extra button in addition to the Bid and Logoff buttons. Once the user presses this button, the application will automatically place a bid
131
that is 100 higher than what other bidders place until it reaches an amount of 10000. Note that this is just a simple illustration using hard-coded values with no exception handling. For example, if several users are using automatic bidding you may have racing conditions where the same amount is being bid almost simultaneously. The first bid will be accepted while the others will be rejected as being too low. Our sample does not handle this; it does not place a higher bid after having one rejected as being too low, but it can be extended to handle this. We leave this to you as an exercise. Currently the bid rejected message is sent as text from the Auctioneer. You may want to change that to a data message and act on it in your code beyond simply writing the message to the transcript window.
I cant send messages to the stage section! To notify users in the stage section, I try to send messages to the section PlaceMember object. I cant set attributes of other sections, even if Im on the stage! I want to set a sections attribute, but it does not work. My user object is on the stage. I do not receive messages sent to a section! I want to listen to messages sent to a section, but it does not work, even when I subscribe to the section
Subscribing to the section event only gives you the SectionListener events (primarily that users enter/leave a section), but not the messages. To listen to the messages, you have to enter the section and you will receive them through the MyMsgListener interface.
132
133
134
6.9 Summary
In this chapter we have taken a stab at solving a real-world problem with our auction house sample. We have taken you through the traditional auction we modeled our solution after. Requirements like being able to communicate in real time, different rights for different groups of people, independence of physical location, and so on, fit very well with the Sametime Place architecture. We described the main classes we created based on the real-world objects and we looked at the flow in the application. We described the implementation further and finally we discussed some of the experiences and considerations we had during our work with the auction house sample. This includes tables that list what you can and cannot do in a place depending on which section you are in and where you are directing your action.
135
136
Chapter 7.
137
138
The example contains an applet that logs into the Sametime community and allows the user to initiate (and receive) instant messages via a Sametime chat session. The applet provides a field to enter meeting invitee names (one or more), a check box to select whether the Invite dialog window is displayed, and a
139
button to initiate the chat. Additional buttons are provided in the actual chat window for sending custom messages, logo graphics are included in the top of the chat window, a text field is included to add a custom signature or tag line, and a custom menu with clickable URLs is added to the chat window.
7.3 CustomizeChatFactory
The CustomizeChatFactory class responds to calls from the CustomizeChatUI components by generating the appropriate component for the request. This class extends the DefaultChatFactory class; it allows a custom user interface to be created/presented.
7.3.1 Methods
The following methods are found in CustomizeChatFactory: CustomizeChatFactory - Constructor Accepts Sametime Session and Applet objects representing the working environment. The Session object is passed to the parent constructor (DefaultChatFactory class). A local Applet object is set to the passed object for reuse. TextSubmitted - Method inherited from TextModifier Interface (implement). This allows chat text to be modified. It intercepts text submitted; the method accepts the text and modifies it accordingly before sending it to the chat area. getTextModifier - Returns TextModifier object from the ChatFrame object passed into it. This is required to provide the ability to manipulate the submitted chat text by way of the TextSubmitted method. getCustomizedPanels - Allows custom panels to be added to the Chat component window. A maximum of three panels can be added either to the top, bottom, or center using constants from the DefaultChatFactory class. The method accepts two parameters:
140
panelPosition - Integer (1-3) of the panel to be added The panelPostion parameter may be one of the following constants from the DefaultChatFactory class: TOP_PANEL top of user panel under menu CENTER_PANEL center of user panel above buttons BOTTOM_PANEL bottom of user panel above status bar
Frame - ChatFrame container for the panels getCustomizedMenu - Used to add a menu bar to the menu. The method accepts one parameter containing the MenuBar object to be manipulated. The default menu items (Meeting and Edit) may not be altered in any way. ShortcutButtons - Used to create the center panel, which includes customized buttons and a check box. getTimeStamp - Used to add a time stamp to the text presented in the chat window. createLogoPanel - Method used to create the panel containing a logo graphic. This method takes advantage of the ImagePanel class to work with the graphic. The method accepts one parameter, the graphic filename. getLogoURL - Method used to returnthe full URL of a logo graphic file. The code base property of the applet is used to calculate the full URL of the graphic. It accepts one parameter containing the logo filename. formatMessage - Method used to properly format chat window text according to user selections (checkboxes). The appropriate text is added to the text by way of user selections and data entered into the signature text field. We now walk through the full source code for the class.
7.3.2 CustomizeChatFactory2.java
The following pages contains the full source listing for the CustomizeChatFactory2 class. We have numbered important lines in the code and we explain those lines after each code element.
141
1.public class CustomizeChatFactory2 extends DefaultChatFactory implements TextModifier { 2. Checkbox checkbox1, checkbox2, checkbox3; 3. final String checkbox1Label = "Add Time Stamp"; final String checkbox2Label = "Include Signature"; final String checkbox3Label = "Include Smiley"; 4. Label label1; 5. final String label1Text = "Signature: "; 6. TextField textfield1; 7. Panel buttonsPanel, signatureSubPanel, signaturePanel, checkboxPanel; 8. Button button1, button2, button3, button4; 9. final String button1Text = "Hey, wanna go to lunch?"; final String button2Text = "I am napping; let's talk later."; final String button3Text = "Boss is approaching, gotta get to work."; final String button4Text = "Time for a break; meet me in the breakroom."; 10. final String button1Label = "Go to lunch"; final String button2Label = "Taking nap"; final String button3Label = "Boss"; final String button4Label = "Break"; 11. final String logoFilename = "logo.gif"; 12. final String smiley = " :) "; 13. final String beginSignature = " [- "; final String endSignature = " -]"; 14. final String urlText1 = "http://www.lotus.com/"; final String urlText2 = "http://www.sametime.com/"; final String urlText3 = "http://www.ibm.com/"; final String urlText4 = "http://www.ibm.com/redbooks/"; final String urlText5 = "http://java.sun.com/"; 15. final String menuLabel1 = "Favorites"; 16. final String menuitemLabel1 = "Lotus"; final String menuitemLabel2 = "Sametime"; final String menuitemLabel3 = "IBM"; final String menuitemLabel4 = "IBM Redbooks"; final String menuitemLabel5 = "Sun Java"; 17. Applet m_applet; 18. Date m_date = new Date();
Explanation of numbered lines: 1. CustomizeChatFactory2 class is declared. It uses the DefaultChatFactory as its base and implements TextModifier to handle working with chat text.
142
2. Java AWT Checkbox objects are created for use on the Chat window. 3. The text associated with the Label objects is set up. This makes it easy to alter the text. 4. Java AWT Label object is created for use in the Chat window. It will be associated with the signature text field. 5. The text for the Label object is set up. 6. Java AWT TextField object is created for use in the chat window. This is used for user entry of signature value. 7. Java AWT Panel objects are created for setting up the chat window interface. 8. Java AWT Button objects are created for use in the chat window. These will be used to send predetermined chat messages. 9. Chat text associated with Button objects is created. This allows the text to be easily altered in one location. 10.Label text for the buttons is created for presentation in the chat window. 11.The filename for the logo used in the chat window is created. 12.String object used to format the text displayed for the smiley checkbox. 13.String object used to format signature text entered by user and displayed in window if appropriate check box is selected. 14.The URLs associated with menu item selections are created. 15.The label for menu added to chat window is created. 16.The text labels for menu items associated with new menu are created. 17.A Java AWT Applet object is used to work with current applet. 18.A Date object is used to add a timestamp to chat text.
Constructor method
19. public CustomizeChatFactory2(STSession session,Applet applet) super(session); m_applet = applet; } {
Explanation of numbered line: 19.The Constructor method calls the constructor of the parent class (DefaultChatFactory) and initializes the Applet object.
TextSubmitted
20. public String TextSubmitted(String text) String tempText = ""; {
143
Explanation of numbered lines: 20.The TextSubmitted method is used to intercept and manipulate chat text before it is posted in the chat window. 21.The formatMessage method is used to manipulate chat text.
getTextModifier
public TextModifier getTextModifier(ChatFrame frame) return this; } {
getCustomizedPanels
22. public Panel getCustomizedPanels(int panelPosition, ChatFrame frame) 23. if (panelPosition == TOP_PANEL) return createLogoPanel(logoFilename); 24. if (panelPosition == CENTER_PANEL) return ShortcutButtons(frame); return null; } {
Explanation of numbered lines: 22.Custom Java AWT Panels are added to the chat window. Sametime allows the addition of three custom panels. 23.A logo panel is added to the top of the chat window. 24.A panel containing buttons, check boxes, and a textfield is added to chat window.
getCustomizedMenu
25. public void getCustomizedMenu(MenuBar mb) { 26. Menu menu1 = new Menu(menuLabel1); 27. MenuItem menuItem1 = new MenuItem(menuLabel1); 28. menuItem1.addActionListener(new ActionListener() { 29. public void actionPerformed(ActionEvent event) { try { 30. m_applet.getAppletContext().showDocument(new URL (urlText1),"target"); } catch (MalformedURLException e) {
144
e.printStackTrace(); } } }); 31. MenuItem menuItem2 = new MenuItem(menuitemLabel2); menuItem2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { m_applet.getAppletContext().showDocument(new (urlText2),"target"); } catch (MalformedURLException e) { e.printStackTrace(); } } }); 32. MenuItem menuItem3 = new MenuItem(menuitemLabel3); menuItem3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { m_applet.getAppletContext().showDocument(new (urlText3),"target"); }catch (MalformedURLException e) { e.printStackTrace(); } } }); 33. MenuItem menuItem4 = new MenuItem(menuitemLabel4); menuItem4.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { m_applet.getAppletContext().showDocument(new (urlText4),"target"); }catch (MalformedURLException e) { e.printStackTrace(); } } }); 34. MenuItem menuItem5 = new MenuItem(menuitemLabel5); menuItem5.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { m_applet.getAppletContext().showDocument(new (urlText5),"target"); }catch (MalformedURLException e) { e.printStackTrace(); } } }); 35. menu1.add(menuItem1);
URL
URL
URL
URL
145
Explanation of numbered lines: 25.Custom menus are added to the chat window. 26.New menu drop-down is created for the chat window. The previously declared text label is used for the menu label. 27.New menu item is created to be added to the custom menu. The previously declared text label is used for the item. 28.ActionListener is added to the new menu item to handle its selection accordingly. 29.The selection of the menu item is handled. 30.Specific URL is opened in a new window from chat window upon selection of the menu item. The previously defined URL address is used. 31.Second menu item is added to the menu and selection is handled. 32.Third menu item is added to the menu and selection is handled. 33.Fourth menu item is added to the menu and selection is handled. 34.Fifth menu item is added to the menu and selection is handled. 35.Menu items are added to the new drop-down menu. 36.Menu is added to the chat window.
ShortcutButtons
37. private Panel ShortcutButtons(ChatFrame frame) { 38. final ChatFrame chatFrame = frame; 39. Panel centerPanel = new Panel(new BorderLayout(4,0)); 40. button1 = new Button(button1Label); 41. button1.addActionListener(new ActionListener() { 42. public void actionPerformed(ActionEvent event) { 43. chatFrame.getChatModel().sendMessage(formatMessage(button1Text)); } }); 44. button2 = new Button(button2Label); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { chatFrame.getChatModel().sendMessage(formatMessage(button2Text)); } });
146
45. button3 = new Button(button3Label); button3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { chatFrame.getChatModel().sendMessage(formatMessage(button3Text)); } }); 46. button4 = new Button(button4Label); button4.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { chatFrame.getChatModel().sendMessage(formatMessage(button4Text)); } }); 47. buttonsPanel =new Panel(new FlowLayout(FlowLayout.LEFT)); 48. buttonsPanel.add(button1); buttonsPanel.add(button2); buttonsPanel.add(button3); buttonsPanel.add(button4); 49. signatureSubPanel = new Panel(new FlowLayout()); 50. textfield1 = new TextField(60); 51. label1 = new Label(label1Text); 52. signatureSubPanel.add(label1); signatureSubPanel.add(textfield1); 53. signaturePanel = new Panel(new BorderLayout()); signaturePanel.add(signatureSubPanel, BorderLayout.EAST); 54. checkboxPanel = new Panel(new FlowLayout(FlowLayout.RIGHT)); 55. checkbox1 = new Checkbox(checkbox1Label); 56. checkbox1.setState(true); checkbox2 = new Checkbox(checkbox2Label); checkbox2.setState(false); checkbox3 = new Checkbox(checkbox3Label); checkbox3.setState(true); 57. checkboxPanel.add(checkbox1); checkboxPanel.add(checkbox2); checkboxPanel.add(checkbox3); 58. centerPanel.add(buttonsPanel,BorderLayout.WEST); centerPanel.add(checkboxPanel, BorderLayout.EAST); centerPanel.add(signaturePanel, BorderLayout.SOUTH); 59. return centerPanel; }
Explanation of numbered lines: 37.Panel is created containing buttons, check boxes, text field, and label. This is the center panel. 38.ChatFrame object is instantiated with passed object. 39.New Java AWT Panel object is created for the chat window center panel.
147
40.Java AWT Button object is instantiated for placement on chat window. The previously declared button label text is used for the button. 41.An actionListener is added to the button. 42.Button selection is handled. 43.Previously declared text is sent to the chat window upon button selection. The formatMessage method is used to properly format text. 44.Second Java AWT Button object is instantiated for placement on chat window. The previously declared button label text is used for the button. 45.Third Java AWT Button object is instantiated for placement on chat window. The previously declared button label text is used for the button. 46.Fourth Java AWT Button object is instantiated for placement on chat window. The previously declared button label text is used for the button. 47.A new Java AWT Panel object is created for previously declared Java AWT Button objects. 48.Buttons are added to the Panel. 49.A new Java AWT Panel object is created for the signature field and label. FlowLayout is used to place objects side by side as placed. 50.Java AWT TextField object is instantiated with a size of 60 (columns). 51.Java AWT Label object is instantiated with previously declared text. 52.Label and TextField objects are added to Panel. 53.New Java AWT Panel object is created for Label/TextField panel. This is used for proper alignment in chat window. 54.New Java AWT Panel object is created for check boxes. 55.Java AWT Checkbox objects are instantiated with previously declared text. 56.Checkbox behavior is set. 57.Checkboxes are added to the new Panel. 58.Panels are added to the main panel created in the method. 59.The main panel is returned by method. This represents the center panel in the chat window.
getTimeStamp
60. private String getTimeStamp() { int hours = m_date.getHours(); int minutes = m_date.getMinutes(); if (minutes < 10) { return hours + ":" + "0" + minutes+ " ";
148
} } }
Explanation of numbered line: 60.The getTimeStamp method assembles a time/date value to be added to a message.
formatMessage
61. private String formatMessage(String msg) { 62. StringBuffer sbMessage = new StringBuffer(msg); 63. if(CustomizeChatFactory2.this.checkbox1.getState()) { sbMessage.insert(0, this.getTimeStamp()); } 64. if(CustomizeChatFactory2.this.checkbox3.getState()) { sbMessage.append(smiley); } 65. if(CustomizeChatFactory2.this.checkbox2.getState()) { sbMessage.append(beginSignature + textfield1.getText() + endSignature); } 66. return sbMessage.toString(); }
Explanation of numbered lines: 61.The formatMessage method centralizes the process of formatting chat text according to custom selections (checkboxes and signature field) in the chat window. 62.StringBuffer object is created to work with morphing text. It is initialized with text passed into method. 63.Timestamp is added to chat text if associated check box is selected. 64.Smiley text is added to chat text if associated check box is selected. 65.Signature text is added from TextField to chat text if associated check box is selected. 66.The newly formatted text is used as the return value of the method. The StringBuffer is converted to a String object via the toString method.
createLogoPanel
67. private Panel createLogoPanel(String logo) { 68. Panel p = new ImagePanel(getLogoURL(logo));
149
69. }
Explanation of numbered lines: 67.The CreateLogoPanel method is used to construct a panel containing an image. 68.The ImagePanel class is used for the logo panel. 69.The background of the logo panel is set.
getLogoURL
70. private URL getLogoURL(String logo) { URL url = null; try { 71. url = new URL(m_applet.getCodeBase(), logo); } catch(MalformedURLException e) { e.printStackTrace(); } return url; } }
Explanation of numbered lines: 70.The getLogoURL method calculates the complete URL for a image file. 71.The logo URL is constructed using the image filename and the applet codebase. In 7.6.2, Loading the logo from a jar file on page 172 we discuss how the logo image can be loaded from a jar file.
7.4 CustomizeChatUI2
The CustomizeChatUI2 class is the Java applet presented to the user. It contains the text field for entering invitees, the list box with meeting type, the buttons, and the check box for including Invite Dialog or not. The class appropriately extends the Applet class. In addition, it implements the methods shown in the next section.
7.4.1 Methods
The following methods are found in CustomizeChatUI2:
150
init - The basic init method of the Applet class. It is called to initialize the applet. The following steps are performed: a. A new Sametime session is created (if necessary) and initialized. b. The initializeLayout method is called to set up Applet user interface. c. The ServerName is calculated from Applet codebase. d. The login name and password are retrieved from applet parameters. e. A new Community Service object is created. f. The ChatUI object is initialized; assigning our CustomizeChatFactory class to it. g. The CommUI object is set up. destroy - The standard destroy method of the Applet class. It is called when the applet (browser) is closed or a new page is loaded. loggedIn - Method triggered when/if user successfully logs onto the Sametime Server. It is derived from the community package, and made available by implementing LoginListener. The send button is enabled upon login. loggedOut - Method triggered when/if user unsuccessfully logs onto the Sametime Server. It is made available by the community package, and implementing LoginListener. The send button is disabled if not logged on. actionPerformed - Handles the action of pressing the send button. Made available by implementing ActionListener. This method parses the invitee names (via breakString method) and calls the Resolve method on the CommUI object for each name. A resolved event is triggered if the names resolve with no problems; otherwise a resolveFailed event is triggered. The Send button is disabled. resolved - Triggered by the actionPerformed event; seeks to resolve invitee names with the Sametime Server. The resolved user names are added to a Vector object; once all names have been resolved a meeting is created with the resolved names. resolveFailed - Triggered when/if names are not resolved; error message is displayed. createMeeting - Triggered by the resolved and resolveFailed methods. The createMeeting method is derived from the base ChatUI class. A one-on-one meeting is created for meetings with only one other user. Otherwise, the meeting type selection (from selection list in applet) is used to start the appropriate meeting. launchMeeting - Meeting is launched from Applet in its own window; this is made available by implementing the MeetingListener interface from the ChatUI package.
151
meetingCreationFailed - Triggered if meeting cannot be created/launched. Made available by the MeetingListener interface from the ChatUI package. UrlClicked - Triggered when URL is clicked. URLs may be presented in the Chat window or in our custom menu selections. The clicking of a URL triggers a URLClickEvent, which triggers urlClicked. This is available by implementing the URLClickListener Interface. InitializeLayout - The initializeLayout method is a custom method added to perform the initial steps of setting up the applet presentation. The choice field is added, along with values added to it. Also, the panels, buttons, and check box are added. The actionListener is assigned to the button, and the default behavior (disabled) for the button is set. BreakString - The breakString method is a custom method for working with the comma separated values in the invitee list entered in the applet by the user. A String array is created and returned containing the separate values. The StringTokenizer object is used to work with the values. We now walk through the full source code for the class.
7.4.2 CustomizeChatUI2.java
The complete code for the class is listed here. We have numbered important lines in the code and we explain those lines after each code element.
152
2. private STSession m_session; 3. private ChatUI m_chatUI; 4. private CommUI m_commUI; 5. private int m_namesToResolve; 6. private Vector m_meetingUsers = new Vector(); 7. private TextField textfield1; 8. private Checkbox checkbox1; 9. private Button button1; 10. private Label label1; 11. private String label1Text = "Invitees names (divided by comma ','):"; 12. private String checkbox1Text = "Show Invite Dialog"; 13. private String button1Text = "Start Chat";
Explanation of numbered lines: 1. The MeetingTypes class is imported to use with selection list applet. The list allows the user to choose the type of meeting to initiate. The constants from the MeetingTypes class are used by the code to create the appropriate meeting. 2. The Sametime Session object is available/used throughout the code. 3. The ChatUI object is used throughout the code; it is used to work with the chat window. 4. The CommUI object is used throughout the code. 5. A primitive integer variable is used to track number of remaining names to be resolved. 6. A Vector object is used to store (resolved) user names for the meeting. 7. The AWT TextField allows the user to enter the invitee names. 8. The AWT Checkbox signals whether the invite dialog box appears after selecting send. 9. The AWT Button initiates the meeting request. 10.The AWT Label object is used to display text in applet. 11.The text for the label is created. 12.The text for the check box is created. 13.The text for the button is created.
init
14. public void init() { try { 15. m_session = new STSession("ChatUIApplet"); 16. m_session.loadAllComponents();
153
17. m_session.start(); } catch(DuplicateObjectException e) { e.printStackTrace(); } 18. initializeLayout(); 19. String serverName = getCodeBase().getHost().toString(); 20. String loginName = getParameter("loginName"); String password = getParameter("password"); 21. CommunityService comm = (CommunityService)m_session.getCompApi(CommunityService.COMP_NAME); 22. comm.addLoginListener(this); 23. comm.loginByPassword(serverName, loginName, password); 24. m_chatUI = (ChatUI)m_session.getCompApi(ChatUI.COMP_NAME); 25. m_chatUI.addUrlClickListener(this); 26. m_chatUI.addMeetingListener(this); 27. m_chatUI.setChatFactory(new CustomizeChatFactory2(m_session, this)); 28. m_commUI = (CommUI)m_session.getCompApi(CommUI.COMP_NAME); 29. m_commUI.addCommUIListener(this); }
Explanation of numbered lines: 14.The init method is standard for applets; it contains initialization logic for the applet. 15.A new Sametime Session is created. 16.All Sametime components are loaded for the session. 17.The Sametime session is started. 18.The initializeLayout method is called to set up the user interface. 19.The Sametime Server name is retrieved via the code base of the applet. 20.The login name is retrieved from an applet parameter (HTML PARAM tag). 21.A new CommunityService object is created. It is a core service required to log in/out of a Sametime Server. 22.A loginListener is added to the CommunityService object. The loggedIn and loggedOut events are fired as necessary. 23.The loginByPassword method of the CommunityService class is called to log in the current user. The login name and password from the applet parameters are used along with the calculated Sametime Server name. 24.A new ChatUI object is created. It allows the creation and participation of Sametime meetings without dealing with the low-level protocols involved. The invite dialog box is included as well. 25.A urlClickListener is added to the ChatUI object to handle the selection of URLs within a chat dialog.
154
26.A meetingListener is added to the ChatUI object. This fosters the creation and launching of Sametime meetings. 27.Our custom Chat factory class is assigned to the ChatUI object. 28.The CommUI object is instantiated. It provides various services, most notable is the ability to resolve names for attending Sametime meetings. This function is used to resolve names entered by user into applet field. 29.A listener is assigned to the CommUI object to handle community UI events.
destroy
30. public void destroy() { 31. CommunityService comm = (CommunityService) m_session.getCompApi(CommunityService.COMP_NAME); 32. comm.logout(); 33. m_session.stop(); 34. m_session.unloadSession(); }
Explanation of numbered lines: 30.The destroy method is a standard applet method. It is executed when the applet is destroyed/unloaded. 31.The CommunityService object is destroyed along with applet. 32.The user is logged out of Sametime Server when applet is destroyed. 33.The Sametime session is stopped when applet is destroyed. 34.All Sametime components are unloaded when applet is destroyed.
loggedIn
public void loggedIn(LoginEvent event) System.out.println("LOGGED IN"); button1.setEnabled(true); } {
loggedOut
public void loggedOut(LoginEvent event) { System.out.println("LOGGED OUT. REASON=" + event.getReason()); button1.setEnabled(false); }
155
actionPerformed
35. public void actionPerformed(ActionEvent p1) { 36. String[] userNames = breakString(textfield1.getText(), ","); 37. if (userNames == null || userNames.length == 0) { return; } 38. button1.setEnabled(false); 39. m_namesToResolve = userNames.length; 40. for (int i=0; i < m_namesToResolve; i++) { m_commUI.resolve(userNames[i].trim()); } }
Explanation of numbered lines: 35.The clicking of the send button is handled. 36.A String array is constructed with invitee names by way of the breakString method. 37.Execution halts if no names are entered. 38.The send button is disabled since it has just been selected. 39.The number of names to resolve is set to the number of values in the String array. 40.All names in the String array are processed with the resolve method.
resolved
41. public void resolved(CommUIEvent event) { 42. STUser resolved = event.getUser(); 43. m_meetingUsers.addElement(resolved); 44. if (--m_namesToResolve == 0) { createMeeting(); } }
Explanation of numbered lines: 41.The resolved method is called if a name is successfully resolved. 42.The username is retrieved from the event. 43.The resolved name is added to the Vector object. 44.The meeting begins if there are no more names to resolve.
156
resolveFailed
public void resolveFailed(CommUIEvent event) { System.out.println("resolve Failed "+ event.getName() + ":" + event.getReason()); if (--m_namesToResolve == 0) { createMeeting(); } }
createMeeting
45. private void createMeeting() { 46. button1.setEnabled(true); 47. String meetingType = "Chat"; if ((meetingType == "Chat") && m_meetingUsers.size() == 1) { 48. STUser imPartner = (STUser)m_meetingUsers.firstElement(); 49. m_chatUI.create1On1ChatById(imPartner); 50. m_meetingUsers.removeAllElements(); return; } MeetingTypes type = null; 51. type = MeetingTypes.ST_CHAT_MEETING;; 52. STUser[] invitees = new STUser[m_meetingUsers.size()]; m_meetingUsers.copyInto(invitees); 53. boolean showInviteDlg = checkbox1.getState(); 54. m_chatUI.createMeeting(type, "", "", showInviteDlg, invitees); m_meetingUsers.removeAllElements(); }
Explanation of numbered lines: 45.The createMeeting method is triggered when all names have been processed/resolved. 46.The send button is reset. 47.The meeting type is set to chat; chat is the only meeting type used in this example. 48.The first username is used if the meeting is a chat or one-on-one meeting. 49.A one-on-one chat session is created. 50.All other users are removed from Vector. 51.Meeting type set accordingly to chat. 52.An array of Sametime users is created using Vector of names.
157
53.The value from the 'show meeting dialog' is retrieved from applet. This signals whether meeting invitee dialog box is shown (True/selected) or not. 54.A new chat Sametime meeting is created.
launchMeeting
55. public void launchMeeting(MeetingInfo meetingInfo, URL url) { getAppletContext().showDocument(url, meetingInfo.getDisplayName()); }
Explanation of numbered line: 55.The launchMeeting method is called when a meeting is started. The meeting is opened in its own window from the applet.
meetingCreationFailed
56. public void meetingCreationFailed(MeetingInfo meetingInfo, int reason) System.err.println("Failed to create the meeting: " + reason); } {
Explanation of numbered line: 56.A message is displayed in the console if the meeting failed to start.
urlClicked
57. public void urlClicked(UrlClickEvent event) { 58. URL url; 59. String urlString = event.getURL(); try { 60. url = new URL(urlString); } catch(MalformedURLException e) { System.err.println("URL Clicked: MALFORMED URL"); return; } 61. getAppletContext().showDocument(url); }
Explanation of numbered lines: 57.The selection of a URL triggers the urlClicked event. 58.A new URL object is created. 59.The URL is retrieved from the event.
158
60.A new URL object is instantiated using the passed URL. 61.The URL is opened in its own window from the applet.
initializeLayout
62. void initializeLayout() { 63. setBackground(Color.white); 64. Panel p = new Panel(new GridLayout(2,1)); Panel p1 = new Panel(); 65. button1 = new Button(button1Text); 66. p1.add(button1); 67. checkbox1 = new Checkbox(checkbox1Text, true); 68. p1.add(checkbox1); 69. Panel p2 = new Panel (new BorderLayout()); 70. label1 = new Label(label1Text); 71. p2.add(label1,BorderLayout.CENTER); 72. textfield1 = new TextField(40); 73. p2.add(textfield1,BorderLayout.SOUTH ); 74. p.add(p2); p.add(p1); 75. add(p); 76. button1.addActionListener(this); 77. button1.setEnabled(false); }
Explanation of numbered lines: 62.The initializeLayout method sets up the applet user interface. 63.The applet background is set as white. 64.New Java AWT Panel object is created for applet layout. 65.A new AWT Button object is created to initiate meeting. The previously declared text is used for the button label. 66.Button is added to the panel. 67.A new AWT Check box object is created for the choice of whether invitee button is displayed in chat window or not. 68.Checkbox is added to the panel. 69.A new AWT Panel is created for label and text fields. 70.Label object is instantiated with previously declared text. 71.Label is added to panel. 72.TextField object is instantiated with size of 40 (columns). 73.TextField is added to panel.
159
74.Panels added to main panel. 75.Main panel is added to the applet user interface. 76.ActionListener is assigned to the send button. 77.Send button is enabled.
breakString
78. protected String[] breakString(String toBreak, String separator) { String[] result = null; if (toBreak != null && !toBreak.equalsIgnoreCase("")) { if (separator == null || separator.equalsIgnoreCase("")) { result = new String[1]; result[0] = toBreak; }else { StringTokenizer stk = new StringTokenizer(toBreak, separator); result = new String[stk.countTokens()]; for (int i = 0; i < result.length && stk.hasMoreTokens(); i++) result[i] = stk.nextToken(); } } return result; }
Explanation of numbered line: 78.The breakString method parses a string value using the specified separator. A StringTokenizer object is used to streamline the process. This concludes our walk though of the CustomizeChatUI2 class. We continue with our last class.
7.5 ImagePanel
The ImagePanel class is a special class for dealing with graphic files (logos) to be presented in one or more panels in our chat interface. The class takes advantage of the AWT package for dealing with the graphics.
7.5.1 Methods
The following methods are found in the ImagePanel class: ImagePanel - The constructor method retrieves the image file via its URL. A MediaTracker object is used to work with the image file.
160
Paint - The image is drawn on the panel. The width and height of both the panel and the image are used to properly present the graphic. getMinimumSize - A Dimension object is returned representing the minimum size for the graphic. The height and width of the image file are used to calcuate the minimum size. getPreferredSize - A Dimension object is returned representing the preferred size; it actually calls the getMinimumSize method and returns its value. We now walk through the full source code for the class.
7.5.2 ImagePanel.java
The complete code for the class is listed here. We have numbered important lines in the code and we explain those lines after each code element.
Explanation of numbered lines: 1. Class is declared; it extends the main AWT Panel class. 2. An Image object is used to work with the logo file. 3. The constructor accepts a URL object. Supposedly, the URL object points to the logo file. 4. The Image object is instantiated with the passed image via its URL. 5. MediaTracker object is instantiated for the current class. It is used to work with media files such as images. 6. The image is added to the MediaTracker object. 7. The code waits for the image to be loaded into MediaTracker object.
161
paint
8. public void paint(Graphics g) { 9. int xPos = (this.WIDTH/2 + m_image.getWidth(this)/2) ; 10. int yPos = (this.HEIGHT/2 - m_image.getHeight(this)/2) ; 11. g.drawImage(m_image, xPos, 0, this); }
Explanation of numbered lines: 8. The paint method displays the image in the Panel. 9. The x coordinate position is calculated using the image and panel dimensions. 10.The y coordinate position is calculated using the image and panel dimensions. 11.The image is displayed.
getMinimumSize
12. public Dimension getMinimumSize() { return new Dimension(m_image.getWidth(this), m_image.getHeight(this)); }
Explanation of numbered line: 12.The minimum size is set using the image settings.
getPreferredSize
13. public Dimension getPreferredSize() { return getMinimumSize(); }}
Explanation of numbered line: 13.The preferred size is set to the minimum size.
Once our three classes are compiled into class files; they are ready to be used in an applet. An applet requires a corresponding HTML file.
162
The code attribute signals the base class for the applet. The base class is the class extending the required Applet class; in our case, this is the CustomizeChatUI2.class file. The archive parameter tells the system where to locate the referenced base class and any other necessary class files. Next, we dive deeper into the issues surrounding applet deployment.
163
classes) in addition to its own class files since the files are not available on the server. Web Server This is the same approach as a plain Domino Server. The files are placed in the appropriate directory (depending on Web Server platform) and the applet archive must be signed to allow it access to the Sametime Server. The applet archive will need all necessary class files (all referenced Sametime classes) in addition to its own class files since the files are not available on the server. Combination of Sametime and Web server HTML files can be placed on a non-Sametime server and the applet's code on the Sametime server. This is done by setting the applet's codebase attribute. Using this approach we do not have to sign the applet. This deployment option is used by the Sametime Links toolkit. You can read more about this in Chapter 12, The Sametime Links toolkit on page 355. Domino-based application The applet files may be placed in a Domino database (as an applet archive) or in a directory. Next, the applet is placed on a Domino design element. The details of this approach are covered in the next section. Locally (hard drive) This is a rare occurrence, but an applet may be used locally. This requires the applet archive (with all required files) and an HTML file for loading the applet. The applet archive/class files must be signed to access the Sametime Server. Now, we take a closer look at integrating our sample into a Domino application.
164
to:
comm.loginByToken(serverName, loginName, password)
The code change is not necessary if you simply want to test adding an applet to Domino, but it is used throughout the remainder of this section so we avoid having to hard code user names and passwords.
Note: We assume some knowledge of Domino development in the following. You will need Domino Designer to add the applet to a Domino database. Make sure that the ID you are using has the right to run agents on the Sametime server. If you just want to make a quick test, you can make a copy of the Sametime server.id file and switch to that ID file in Domino designer.
The applet will be added to the basic Discussion application/database that is included with all Domino installations. The applet will be added to the upper portion of the main discussion form displayed in Figure 7-3 on page 165.
The applet will be added to the top of the form above the subject field. This allows users to initiate a chat from the form. The first step is the creation of the Domino database container. Figure 7-4 shows the New Database dialog box presented in the Domino Designer client (CTRL-N). This allows the server, database title, database path/filename, template, and other options to be chosen.
165
For this example, the database was created locally with the specified title and filename. The Discussion Notes & Web (R5.0) template was used to create it. This provides a complete application with main topic as well as response forms. The applet will be added to the Main Topic form, but first an applet resource is created. This allows the applet to be easily reused and maintained from a central location. Figure 7-5 shows the list of resources for the database, with Figure 7-6 showing the result of choosing the New Applet Resource button.
166
The necessary archive file (which we called uitest.jar) is chosen from the file system. The creation of the jar file depends upon your environment; see Chapter 4, Installation and setup on page 71 for details on how to create a jar file from the IBM JDK or from VisualAge for Java. Select the OK button and a dialog box opens as shown in Figure 7-7. It accepts a name for the resource.
The customized chat window displays a logo. Normally this is a gif file that is loaded from the directory where the applet was loaded from. To make it available for an applet embedded in Domino we must add it as an Image resource. Click on Images under Resources in Domino Designer and import the logo.gif file as a resource. The next task is the placement of the applet on the proper form. The Main Topic form has been chosen, but it may be placed on others or all forms. Figure 7-8 on page 168 shows the Main Topic form opened in Domino Designer.
167
The applet is placed above the subject field. A new row is inserted in the table for the applet. The cursor is placed at the point of insertion, and the pass-thru HTML for the applet is inserted. You format text as pass-thru HTML by highlighting it and selecting Text -> Pass-Thru HTML in Domino Designer. Figure 7-9 on page 169 shows the Main Topic form with the necessary pass-thru HTML.
168
The Domino database and form must be properly configured to work with Sametime. We will cover these steps before dissecting the pass-thru HTML. Basically, three tasks should be completed to prepare the application for Sametime integration. These tasks are covered in more detail in Appendix A of the Sametime 2.5 Java Toolkit Developers Guide. The following tasks must be completed. 1. Copy the following Script Libraries from the Sametime Discussion (STDisc50.ntf) template installed with Sametime to our application. SametimeHost contains code to retrieve/calculate the Sametime Server address/name SametimeAgent SametimeClient SametimeStrings contains Sametime-relevant variables/constants. 2. Copy the following agents from the Sametime Discussion (STDisc50.ntf) template to our application. SametimeGetHost populates the SametimeHost field on the form; uses SametimeHost library. SametimePreWebQueryOpen SametimeWebQueryOpen
169
3. Add the following three lines to the WebQueryOpen event for the Main Topic form. These lines run the previously listed agents when the form is opened in a browser client.
@Command([ToolsRunMacro]; "SametimePreWebQueryOpen"); @Command([ToolsRunMacro]; "SametimeWebQueryOpen"); @Command([ToolsRunMacro]; "SametimeGetHost")
Pass-thru HTML is used to place the applet on the form. Domino Designer does allow the embedding of Java applets via the Create->Java Applet menu selection. This approach causes problems with our applet, since the applet CODEBASE attribute is populated via a Domino field from our form. The embedding of applets on a form does not make the CODEBASE attribute accessible to be populated by a field value. It is only accessible via the properties info box for the embedded applet. For this reason, pass-thru HTML is used to properly set up our applet on the form. The complete pass-thru HTML follows:
<APPLET CODE="CustomizeChatUI.class" ARCHIVE="stchatui.nsf/ChatUI/$File/uitest.jar, sametime\toolkits\java\Bin\CommRes.jar, sametime\toolkits\java\Bin\STComm.jar" CODEBASE="http://[SametimeHost field]/" HEIGHT="100" WIDTH="300"> <PARAM NAME="loginName" VALUE="[loginName field]"> <PARAM NAME="password" VALUE="[SAMETIME_TOKEN field]"> </APPLET>
The listed HTML includes three new Domino fields, described in Table 7-1.
Table 7-1 New fields on Sametime-enabled Domino form
Field name SametimeHost Description Holds the server or host name; the field is computed-for-display with a default value of empty quotes (""). The WebQueryOpen event populates the field. Holds user name in Sametime; the field is computed-for-display with a default value of @UserName. The token used for Sametime login instead of the user password; the field is computed-for-display with a default value of empty quotes ("");
loginName SAMETIME_TOKEN
170
The three fields are inserted into the body of the applet pass-thru HTML. Here is a breakdown of the rest of the applet pass-thru HTML. CODE the applet base class. ARCHIVE the Java archive (jar) containing classes/resources; the value points to the previously created Java applet resource as well as the required Sametime toolkit jar files. WIDTH width of displayed applet. HEIGHT height of displayed applet. CODEBASE the starting point for located resources (archive). The applet is ready to be used after adding it to the form. Figure 7-10 shows a Main Topic loaded in a browser with the customized chat window open.
171
Tip: In this sample we followed the instructions from the Sametime 3.0 Java Toolkit Developers Guide, where you are instructed to use the Sametime-enabled teamroom template.
Instead, you can start with the Sametime Auth Skeleton (stskel.ntf) template, which is a minimized token-generation application. Follow these steps to modify the application: 1. Create a .nsf, and remove the design inheritance from the template. 2. Modify the access control to force authentication, and test the application. 3. Modify the Default form by replacing the embedded applet with your applet. 4. Provide needed parameters and test.
Tip 2: If you would rather get a token and feed it to a Sametime application yourself, you can modify the Domino database further by adding a form that can serve up the token as a response to an HTTP command like:
http://<sametime server>/stapp.nsf/GetUserAndToken?OpenForm
Tip 3: Our Domino Sametime deployment sample requires that the client is a Web browser. If you want to Sametime-enable Domino application for Lotus Notes clients you can still launch an applet. However, the agent we use to get the token is only triggered for Web browsers, so you will have to change the logic if you want to use an applet. A better approach, in many cases, is to use the Sametime COM toolkit instead. We discuss that toolkit in Chapter 11, The COM toolkit on page 309.
172
To load the image from the jar file we must instead use a line like this:
URL url = CustomizeChatFactory.class.getResource("logo.gif");
Using this approach gives you one file less to keep track of.
173
Init This method performs setup operations for the user interface. The user interface is a window with labels and field for the user data. The AWT package is utilized. LoadAttributes This method must be used per the StorageServiceListener interface. It is fired from the serviceAvailable event/method telling the system to load values/attributes from storage. loadValuesFromAttributes Values are loaded from the service. The values are loaded as one string, and parsed (on semi-colon). The resulting values are stored in a Vector object and assigned to fields set up in the init method. The position number in the Vector corresponds to a field in the user interface. serviceAvailable This method must be used per the StorageServiceListener interface. It is fired upon the initialization of the Storage Server; signalling it is available. serviceUnavailable This method must be used per the StorageServiceListener interface. It is fired upon the initialization of the Storage Server; signalling it is not available. storeSettings The storeSettings method is called when the user interface (window) is closed; this ensures any changes to the values are properly stored for reuse next time the user accesses the application. The complete code of the RedStorageFrame class is listed next.
7.7.1 RedStorageFrame.java
Here is the source code for our RedStorageFrame class. We have numbered important lines in the code and we explain those lines after each code element.
174
3.
4.
private StorageService m_storageService; private TextField button1TextField; private TextField button2TextField; private TextField button3TextField; private TextField button4TextField; private TextField button1MessageTextField; private TextField button2MessageTextField; private TextField button3MessageTextField; private TextField button4MessageTextField; private Integer m_nReqID; private final static int ATT_KEY = 0xFFFF;
Explanation of numbered lines: 1. The storage package is imported; it contains all necessary classes for dealing with the storage service. 2. The class is declared; it implements the StorageServiceListener interface to work with the storage service. 3. A new storage service object is created. 4. An attribute key is needed to work with data in via the storage service.
6. 7. 8. 9.
Explanation of numbered lines: 5. The constructor method contains code for instantiating the storage service for our use. 6. The storage service object is instantiated. 7. A storage service listener object is assigned to our class.
175
8. A window listener is added to respond accordingly when the window is closed. 9. The attributes are stored upon window closing.
attrQueried
public void attrQueried ( StorageEvent e ) { if (m_nReqID == e.getRequestId()) { 10. if (e.getRequestResult() == STError.ST_OK) { 11. String temp = e.getAttrList().toString(); 12. int tempInt = temp.lastIndexOf(":"); 13. String temp3 = temp.substring(tempInt+1, temp.length()-1); 14. loadValuesFromAttribute(temp3); } else { System.out.println("Could not load attributes."); } } }
Explanation of numbered lines: 10.Execution proceeds only if no error is incurred. 11.The attribute list is retrieved as a string. 12.We need the data to the right of the last colon, so the colon is located. The attribute uses the following form:
[key:65535 value: text]
where the key is assigned upon storage and the text is the actual attribute value. We only need the text. 13.The text is extracted using the location of the colon as the starting point and proceeding to the character before the final brack (]). 14.The extracted text is sent to the loadValuesFromAttribute method to populate the user interface.
attrStored
public void attrStored ( StorageEvent e ) { if ((m_nReqID == e.getRequestId()) && (e.getRequestResult() != STError.ST_OK)) { System.out.println("Could not store attributes."); } }
176
attrUpdated
public void attrUpdated ( StorageEvent e ) { }
Note: We are setting up four buttons, four text fields, and so on in the init methods. This means that there is a lot of repetitive code. We show you the code for the first button, for the first entry field, and so on, and remove the repetitive code for the additional elements.
init
public void init ( ) { this.setLayout ( new BorderLayout() ); button1TextField = new TextField ( "button1" ); ..... button1MessageTextField = new TextField ( "MessageText1" ); ..... Label button1Label = new Label ( "Button 1" ); ..... Label button1MessageLabel = new Label ( "Message 1" ); ..... Panel button1Panel = new Panel ( new BorderLayout() ); ..... Panel tPanel1 = new Panel ( new FlowLayout() ); ..... tPanel1.add ( button1Label ); tPanel1.add ( button1TextField ); tPanel1.add ( button1MessageLabel ); tPanel1.add ( button1MessageTextField ); ..... button1Panel.add ( tPanel1 , BorderLayout.CENTER ); ..... Panel midPanel = new Panel ( new BorderLayout() ); midPanel.add ( button2Panel, BorderLayout.NORTH ); midPanel.add ( button3Panel, BorderLayout.SOUTH ); add ( button1Panel , BorderLayout.NORTH ); add ( midPanel , BorderLayout.CENTER ); add ( button4Panel , BorderLayout.SOUTH ); pack(); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((int)( ( d.width - getSize().width ) /2 ) , (int)( ( d.height - getSize().height ) /2 ) ); }
177
loadAttributes
public void loadAttributes ( ) { 15. m_nReqID = m_storageService.queryAttr(ATT_KEY); }
Explanation of numbered line: 15.The firing of the loadAttributes method triggers the query of our attribute (assigned key) from the storage service.
loadValuesFromAttribute
public void loadValuesFromAttribute ( String attr ) { if (attr.equalsIgnoreCase("")) { 16. button1TextField.setText("button1"); button2TextField.setText("button2"); button3TextField.setText("button3"); button4TextField.setText("button4"); button1MessageTextField.setText("button message 1"); button2MessageTextField.setText("button message 2"); button3MessageTextField.setText("button message 3"); button4MessageTextField.setText("button message 4"); } else { String attribute; String value; 17. Vector values = new Vector(); 18. StringTokenizer tokenizer = new StringTokenizer(attr,";"); 19. while ( tokenizer.countTokens() > 0 ) { 20. values.addElement (tokenizer.nextToken()); } if ( values.size() > 0 ) { 21. button1TextField.setText((String)values.elementAt(0)); if ( values.size() > 1 ) button2TextField.setText((String)values.elementAt(1)); if ( values.size() > 2 ) button3TextField.setText((String)values.elementAt(2)); if ( values.size() > 3 ) button4TextField.setText((String)values.elementAt(3)); if ( values.size() > 4 ) button1MessageTextField.setText((String)values.elementAt(4)); if ( values.size() > 5 ) button2MessageTextField.setText((String)values.elementAt(5)); if ( values.size() > 6 ) button3MessageTextField.setText((String)values.elementAt(6)); if ( values.size() > 7 )
178
button4MessageTextField.setText((String)values.elementAt(7)); } } }
Explanation of numbered lines: 16.Text fields are added to the window with default values if no values are found in the storage service. 17.If the attribute does contain values, a Vector object is created to stored the parsed values. 18.A StringTokenizer object is created using the attribute string and a semicolon as the separator. 19.The resulting StringTokenizer object is traversed with each value stored in the Vector. 20.Individual values are stored in the Vector. 21.Vector elements are used to populate user interface text fields.
serviceAvailable
public void serviceAvailable ( StorageEvent e ) { 22. loadAttributes(); }
Explanation of numbered line: 22.The loadAttributes method is called if the storage service is available.
serviceUnavailable
public void serviceUnavailable ( StorageEvent e ) { System.out.println("Storage Service is unavailable."); }
storeSettings
public void storeSettings ( ) { 23. StringBuffer buffer = new StringBuffer(); 24. buffer.append(button1TextField.getText()); 25. buffer.append(" ; "); buffer.append(button2TextField.getText()); buffer.append(" ; "); buffer.append(button3TextField.getText()); buffer.append(" ; "); buffer.append(button4TextField.getText());
179
26. 27. } }
buffer.append(" ; "); buffer.append(button1MessageTextField.getText()); buffer.append(" ; "); buffer.append(button2MessageTextField.getText()); buffer.append(" ; "); buffer.append(button3MessageTextField.getText()); buffer.append(" ;"); buffer.append(button4MessageTextField.getText()); STAttribute attribute = new STAttribute(ATT_KEY, buffer.toString()); m_nReqID = m_storageService.storeAttr(attribute);
Explanation of numbered lines: 23.A new StringBuffer object is created to store our values. 24.Text from each TextField is added to the StringBuffer object. 25.Individual values are separated by semicolons. 26.A new STAttribute object is created with the value in the StringBuffer object. It contains all data values separated by semicolons. 27.The new attribute is stored.
180
Notice that the code is similar to its counterpart in the RedStorageFrame class, but the values are used differently. Also, the values are not altered in the chat dialog window. This is only available from the applet initiating the chat session. The new user interface is displayed in the next two figures. The applet interface has a new button to launch the RedStorageFrame for working with stored values, as shown in Figure 7-11.
181
You must supply an instance of your chat factory class as a parameter to the setChatFactory method in the chatui object. If you want to make a quick test of this you can modify the LiveNames Applet sample included with the toolkit to use your CustomizeChatUI.
182
We did this and found out that our initial choice of adding the Storage Service button to the launch window was not such a great idea because we do not use this window when the chat ui is launched from one of the other toolkit UI objects. However, the button can easily be moved to the chat window, or you can simply invoke the window (RedStorageFrame) for setting your predefined messages from a menu option. We will leave it as an exercise for you to do this modification.
Note: We modifed the LiveNames sample to test the new chat UI with VisualAge for Java 4.0. However, we initially got an error when we added the import statement for the chatui package to the LiveNamesApplet class. The error message said that the ResolveListener was ambiguous. When we looked in the chatui package in VisualAge for Java, there indeed was a ResolveListener in that package (as well as in the lookup package where it is supposed to be). We deleted the ResolveListener from the chatui package and the sample worked, but read on because this is not a good solution.
If you use the IBM JDK to compile the sample, no errors will be reported. The problem seems to be due to a bug in VisualAge for Java. There actually is a ResolveListener interface in the chatui package, but it is not public and thus not accessible for the Java toolkit developers. VisualAge for Java should not make the ResolveListener in the chatui package visible.
However, deleting the ResolveListener will cause the Sametime toolkit to break down in other places.
If you want to try our workaround in VisualAge for Java, make sure that you have versioned the classes in the Sametime chatui package first. Bring back the versioned edition of the chatui package before you attempt any other Sametime development. We will now go on to our last example in this chapter, where we pass a Sametime token between two applets.
183
into Sametime, and to avoid having the user prompted every time a new applet is launched you have to figure out how to get the token from the first applet and pass it on to the other applets in the application. In this section we walk through an example of doing this. In this section we cover the details of passing login data between applets. One applet will load and log into a Sametime Server, and the login information will be made available to other applets (Web pages). The key piece of information is the token. A token for a user can be accessed by way of the Token service (com.lotus.sametime.token package). A class implements the TokenServiceListener interface with the following methods for dealing with tokens: generateTokenFailed - This method/event fires if a call to the generateToken method fails to produce a token. The only parameter is a TokenEvent object. tokenGenerated - This method/event fires upon a successfully generateToken call. The one parameter is a TokenEvent object; it provides access to the specifics of the generated token. Our RedCustomizeChatUI application is used to demonstrate the specifics of dealing with tokens. In addition to the token, the class will also make the username and server available via get methods. These methods are accessible from JavaScript. The changes to the RedCustomizeChatUI class are listed first. The changes consist of implementing TokenServiceListener, adding the required methods, calling generateToken from the login method, and adding fields for the data values and corresponding get/set methods. The class code is listed next, with changes highlighted and explained. We only show new or changed methods. For an explanation of other methods in the class, see 7.4.2, CustomizeChatUI2.java on page 152.
184
1. import com.lotus.sametime.token.*; public class RedCustomizeChatUI extends Applet implements MeetingListener, UrlClickListener, CommUIListener, LoginListener, ActionListener, 2. TokenServiceListener { private STSession m_session; private ChatUI m_chatUI; private CommUI m_commUI; private int m_namesToResolve; private Vector m_meetingUsers = new Vector(); private TextField textfield1; private Button button1; private Checkbox checkbox1; private Label label1; private String label1Text="Invitees (divided by comma ','):"; private String checkbox1Text = "Show Invite Dialog"; private String button1Text = "Start Chat"; private StorageFrame m_attributeFrame; private Button storageButton; 3. private TokenService m_tokenService; 4. private String token = ""; private String username; private String serverName = "";
Explanation of numbered lines: 1. The necessary token service classes are made available in our code. 2. The TokenServiceListener is added to our class. 3. A TokenService object is created for later use. 4. The member fields are declared; these will hold values for username, token, and server. They are declared as private to prevent direct access. A corresponding get method is provided for each field.
5.
generateTokenFailed
6. public void generateTokenFailed( TokenEvent e ) { System.out.println("Generate Token Failed."); System.out.println(e.toString()); }
185
Explanation of numbered line: 6. The generateTokenFailed event fires when/if a call to the generateToken method is unsuccessful.
getServerName
7. public String getServerName() { return serverName; }
Explanation of numbered line: 7. A public get method for the server name field is provided. It returns the value stored in the serverName field.
getToken
8. public String getToken() { return token; }
Explanation of numbered line: 8. A public get method is provided for the token field. It returns the value stored in the token field.
getUsername
9. public String getUsername() { return username; }
Explanation of numbered line: 9. A public get method is provided for the username field. It returns the value stored in the username field.
handleException
private void handleException(Throwable exception) { exception.printStackTrace(System.out); }
186
init
public void init() { try { m_session = new STSession("ChatUIApplet"); m_session.loadAllComponents(); m_session.start(); }catch(DuplicateObjectException e) { e.printStackTrace(); } initializeLayout(); 10. setServerName ( getCodeBase().getHost().toString() ); String loginName = getParameter("loginName"); String password = getParameter("password"); CommunityService comm = (CommunityService) m_session.getCompApi(CommunityService.COMP_NAME); comm.addLoginListener(this); comm.loginByPassword(this.getServerName(), loginName, password); m_chatUI = (ChatUI)m_session.getCompApi(ChatUI.COMP_NAME); m_chatUI.addUrlClickListener(this); m_chatUI.addMeetingListener(this); m_attributeFrame = new RedStorageFrame(m_session); m_attributeFrame.setVisible(false); m_chatUI.setChatFactory( new CustomizeChatFactory(m_session, this )); m_commUI = (CommUI)m_session.getCompApi(CommUI.COMP_NAME); m_commUI.addCommUIListener(this); 11. m_tokenService = (TokenService) m_session.getCompApi(TokenService.COMP_NAME); 12. m_tokenService.addTokenServiceListener( this ); }
Explanation of numbered lines: 10.The private set method for the serverName field is called with value from the current environment. 11.The TokenService object is instantiated. 12.The TokenService listener is assigned to our class. This activates the generateTokenFailed and tokenGenerated methods. They await a call to generateToken.
loggedIn
public void loggedIn(LoginEvent event) { button1.setEnabled(true); 13. m_tokenService.generateToken(); }
187
Explanation of numbered line: 13.The generateToken method is called when/if a user successfully logs onto the Sametime Server. This call triggers the other related events: either tokenGenerated or generateTokenFailed.
setServerName
14. private void setServerName(String newServerName) { serverName = newServerName; }
Explanation of numbered line: 14.The private set method for the serverName field. The method is declared private to prevent outside access.
setToken
15. private void setToken(String newToken) { token = newToken; }
Explanation of numbered line: 15.The private set method for the token field.
setUsername
16. private void setUsername(java.lang.String newUsername) { username = newUsername; }
Explanation of numbered line: 16.The private set method for the username field.
tokenGenerated
17. 18. 19. 20. public void tokenGenerated( TokenEvent e ) { Token token = e.getToken(); setToken(token.getTokenString()); setUsername(token.getLoginName()); }
188
Explanation of numbered lines: 17.The tokenGenerated method is triggered if a call to the generateToken method is successful. 18.A Token object is populated by way of the TokenEvent object passed into the method. 19.The token field of the current object is set. 20.The username field of the current object is set. The class may now be used (with other relevant classes) in a Web page, with JavaScript providing access to the new fields. Public get and set methods can be accessed via the applets property of the JavaScript document object. The applets property is actually an array (beginning with zero) containing references to all applets contained in a page.
Explanation of numbered lines: 1. A script block is declared for inclusion in an HTML page. Our script will be placed in the HTML head section.
189
2. The variables are declared for accessing the class field values. 3. The loadValues function accesses the applet field values via the appropriate get methods. 4. Our applet is accessed; it is the first applet on the page. 5. The variables are populated with empty values if the applet was not found. 6. The username variable is populated via the applet getUsername method. 7. The token variable is populated via the applet getToken method. 8. The serverName variable is populated via the applet getServerName method. 9. The showApplet method tests our loadValues method by displaying the variable values in a popup window. 10.The loadValues function is called. 11.The variable values are displayed in a popup window. The JavaScript may be utilized with a button on a page. The following HTML accomplishes the feat.
Now, the complete HTML for our page is displayed. It includes the JavaScript functions and our applet. One important note is the inclusion of the MAYSCRIPT option (highlighted) in the applet declaration. This makes the applet available to JavaScript.
<HTML> <HEAD> <TITLE>CustomizeChatUI</TITLE> <SCRIPT LANGUAGE="JavaScript"> var username; var token; var serverName;
190
function loadValues() { var app = document.applets[0]; if (app == null) { username = ""; token = ""; serverName = ""; } else { username = app.getUsername(); token = app.getToken(); serverName = app.getServerName(); } } function showApplet() { loadValues(); alert("User: " + username + "\nToken: " + token + "\nServer: " + serverName); } </SCRIPT> </HEAD> <BODY> <APPLET CODE="CustomizeChatUI.class" WIDTH="350" HEIGHT="200" NAME="CustomizeChatUI" MAYSCRIPT> <param name="archive" value="export.jar, STComm.jar, CommRes.jar"> <param name="loginName" value="ctester"> <param name="password" value="password"> </APPLET> <FORM> <INPUT NAME="Applet" VALUE="Applet Properties" TYPE="Button" onClick="showApplet();"> </FORM> </BODY> </HTML>
The next two figures show the results of first loading the applet (with button) and the selection of the button.
191
192
private void login() { m_comm = (CommunityService) m_session.getCompApi(CommunityService.COMP_NAME); m_comm.addLoginListener(this); m_comm.loginByToken( getParameter("serverName") , getParameter("loginName") , getParameter("password") ); }
Note: In this example we want to test login by token, so we simply replace the login method. In production you may want your applet to be more flexible: to allow login both by id/password and by token. If you look at our source code from Chapter 6, A place-based auction example on page 101 you can see an example that allows both ways of login (and in addition opens a login dialog if no login parameters are passed along).
Our sample HTML is altered as well. A new JavaScript function is added to load the sample awareness applet. The function is listed next.
function loadAwareness() { 1. loadValues(); 2. document.writeln("<HTML>"); document.writeln("<HEAD>"); document.write("<TITLE> "); document.write("Toolkit Sample Awareness Applet"); document.writeln("</TITLE>"); document.writeln("</HEAD>"); document.writeln("<BODY BGCOLOR=\"C0C0C0\">"); 3. document.write("<APPLET CODE=AwarenessApplet.class ); document.write("ARCHIVE=\aware.jar\); document.write(" WIDTH=\250\ ); document.writeln(" HEIGHT=\300\>"); document.write("<param name=\"archive\" ); document.writeln("value=\"aware.jar,CommRes.jar,STComm.jar\">"); 4. document.writeln("<param name=\"loginName\" value=\"" + username + "\">"); 5. document.writeln("<param name=\"password\" value=\"" + token + "\">"); 6. document.writeln("<param name=\"serverName\" value=\"" + serverName + "\">"); document.writeln("</APPLET>"); document.writeln("</BODY>"); document.writeln("</HTML>"); }
193
Explanation of numbered lines: 1. The loadValues method is called to populate the required variables. 2. The writeln method of the JavaScript document object writes the specified text to the browser window. The written material overwrites the current browser content. 3. The HTML is displayed for the awareness applet. 4. The loginName parameter is written with the username variable as the value. 5. The password parameter is written with the token variable as the value. 6. The serverName parameter is written with the serverName variable. The loadAwareness applet loads the specified HTML in the currently open browser window. Another button is added to our page for calling our new function.
<INPUT NAME="Applet" VALUE="Awareness" TYPE="Button" onClick="loadAwareness();">
The new button and JavaScript function are added to our HTML file. The results are displayed in the next two figures.
194
If you click on the button labelled Awareness in Figure 7-16, the applet shown in Figure 7-17 will be launched and login will be performed by token.
Figure 7-17 Applet launched by other applet using token for login
One applet was launched, accepting the login information submitted from another applet. Applet-to-applet communication is critical to a smooth user experience. We do not want our user community repeatedly entering authentication information. They will become annoyed and it may lead to an application not being used.
195
Note: Passing token between applets may not be the best approach, but it is an easy way to let separate applets work in a concerted manne, as you can see. If you are designing a complex Sametime application (versus using different existing components) you should consider having one component on the client handle the authentication and then let the other Sametime components get their login information from the authentication component. Yet another approach is to handle it on the server side, possibly together with single sign-on to other systems like e-commerce, CRM, and so on. To learn more about what you can do on the server side see the redbook Working with the Sametime Community Server Toolkit, SG24-6667.
7.10 Summary
In this chapter we have focused on the ability to create your own customized chat window with the Sametime Java toolkit. During our work with the sample we have also shown you how to use the storage and token services in Sametime. In addition we have walked through how a Sametime applet can be deployed via a Lotus Domino database.
196
Part 3
Part
C++ toolkit
197
198
Chapter 8.
199
8.1 Overview
The Sametime 3.0 C++ Toolkit is a collection of static library components that allow developers to enhance Windows applications with Community Services, such as awareness and instant messaging. It is based on the same toolkit architecture as the Sametime 3.0 Java Toolkit. However, the C++ toolkit supports only the Community Services (that means no Meeting Services), and does not provide any UI components. It is geared towards allowing developers to quickly and easily Sametime-enable any Win32- or MFC-based Windows application. Although applications developed with this toolkit will work when run against a Sametime 1.5 server, any toolkit services that require features new to Sametime 3.0 (that is, Storage and Place services), will not function when a Sametime 3.0 server is not available. The C++ Toolkit uses only Standard Type Library (STL) containers and is therefore standard-based. The C++ Toolkit is not based on Microsoft Foundation Classes (MFC) or Object Linking and Embedding (OLE) technologies. If needed, these technologies can be used as a second layer on top of the toolkit. The toolkit has a layered architecture with two different layers: The Transport Layer The layer through which all communication with the Sametime server passes. The Service Layer The layer providing access to the Sametime Community Services.
200
The toolkit is modular, thread-safe, and extendable, providing an object-oriented API, which exposes the entire set of Community Services features provided by Sametime 3.x. The Sametime 3.0 C++ Toolkit supports the Microsoft Visual C++ 6.0 Developer Studio SP5.
8.1.1 Modular
The Sametime 3.0 C++ Toolkit is component-based, and the different services it provides are divided among many components. Each component is a separate static library. The developer links into the final project only the binary libraries of the components needed.
8.1.2 Thread-safe
The Sametime 3.0 C++ Toolkit is thread-safe so that a Sametime-enabled application built on top of the Toolkit can have many threads. Calls to the Toolkit API can come from different threads; the Toolkit handles multiple threads without requiring additional work by the developer.
201
8.1.3 Extendable
In the Sametime 3.0 C++ Toolkit, developers can add components that provide new services and a new User Interface (UI), making the Toolkit expandable by both core Sametime developers and by third-party developers. This chapter does not describe how to write such components. However, future documentation will include this information.
Community services
Table 8-1 lists and describes the community services available in the Sametime 3.0 C++ Toolkit.
Table 8-1 Community services
Name of service Community service Description A required component in almost any use of the Toolkit. It allows you to log in, log out, and make changes to online status, online attributes, and privacy settings. Provides notifications of changes in the online status and attributes of users in the community. This service is sometimes referred to as People Awareness. Provides one-to-one communication between clients. It is used mostly for Instant Messaging chat between users, but it can also be used to exchange any kind of text or binary data. Provides the ability to create virtual meeting places that users can enter or leave, see who is in the meeting place, and share activities. This service is sometimes referred to as Place-Based Awareness, and it is a major cornerstone of the Sametime 3.0 architecture.
Awareness service
Places service
202
Description Provides server-side storage of user-related data. A user can access his personal data from any Sametime-enabled application. For example, Sametime Connect uses this service to store a users Buddy List on the Sametime server so the user can access it regardless of where he logs in. Provides name resolving and group content lookup. Provides directory-browsing services. Allows posting a message to many users at once. Allows generation of temporary login tokens.
203
The Sametime 3.0 C++ toolkit is provided as an installable package. Table 8-2 describes the installed directory structure.
204
samples
Note: The samples provided with the C++ toolkit assume the required include files are stored in directories under the inc directory. However, the zip file with toolkit binaries that we downloaded from the Sametime server had the directory structure reversed (so that those directories that should be under the inc directory were top level directories and each of them had a sub directory called inc. Thus you may have to re-arrange your directory structure if this also happens to you.
Note: Another thing we encountered when trying to compile our first sample program was a missing lib file from the toolkit binaries. Even though the file has not been included in the toolkit zip file (as it should be), it still is on the Sametime server. If you also encounter this problem simply search for the file on the Sametime server.
205
Win32Status Sample
You can also download a PDF file with a poster of the Sametime community services C++ object model from the C++ toolkit section on the Sametime server.
8.3 Summary
In this chapter we introduced you to the Sametime C++ community toolkit. We described how you get the toolkit, and pointed you to additional samples and information provided with the toolkit.
206
Chapter 9.
207
The source code for the full application is available from the IBM Redbooks Web site. See Appendix E, Additional Web material on page 407 for instruction on how to get the material.
9.1 Overview
As a foundation, we will use the BasicBuddyList sample from the Tutorial for the C++ toolkit. The BasicBuddyList provides a simple awareness-based instant messaging application. You may want to read or at least browse the description of the BasicBuddyList in the C++ toolkit tutorial since we are assuming familiarity with that sample. In this chapter we show how to extend the BasicBuddyList sample to create more complex Chat, Audio Video, Application Sharing and WhiteBoard Meeting applications using the C++ 2.0 Toolkit. We show you how to create full Sametime meetings, starting from a Chat Meeting, and advancing to more complex meetings like Audio and Video meetings using the Post, Places and Token services. Figure 9-1 shows the Meetings sample main dialog, where you can just add users, right-click on one or more of them, and select the meeting to which you want to invite the user or the users.
208
Figure 9-2 shows the Invitation dialog, where you prepare your invitation and send it to all users you have selected.
Figure 9-3 on page 210 shows the chat meeting (also called N-Way chat) dialog, where we can send and receive chats among several participants.
209
Now you can even upgrade your meeting to a video meeting, for example, by adding tools, as shown in Figure 9-4.
210
As you can see in Figure 9-5, once the tools are added, an Audio - Video meeting will be launched.
211
Listener
MyMeetingUIPlacesServiceListener
MeetingDlg
MyMeetingUITokenServiceListener
MeetingUI InviteUIDlg
MyPostServiceListener
Place
Places Service
Token Service
Post Service
Post
MyPlaceListener
MyMeetingControllerTokenServiceListener
MeetingController MyUserInPlaceListener
MySectionListener
212
MyMeetingUIPlacesServiceListener
MeetingDlg
MyMeetingUITokenServiceListener
MeetingUI InviteUIDlg
MyPostServiceListener
Place
Post Service
Post
213
General services
The MeetingUI class will expose the following methods to other classes for creating meetings, opening A/V help and opening the A/V wizard as you can see in Table 9-2.
Table 9-2 MeetingUI General Services
Methods void CreateMeeting(list<STWatchedUser*> lstUsers,long meetingType) void OpenAVHelp() void OpenAVWizard() bool IsMeetingsServiceAvailable() Description Creates Sametime meeting with the specific list of users and the specific meeting type (see Table 9-1). Opens the Sametime Audio/Video Help. Launches the Audio/Video Wizard. Returns true/false if the Place Service is available, so meeting can be created.
214
MeetingUI.h
The MeetingUI.h file is shown in Example 9-1.
Example 9-1 MeetingUI.h
#if !defined __MEETING_UI__ #define __MEETING_UI__ #pragma warning (disable: 4786) #include <list> #include <string> class class class class class class class class class class class class class STSession; PostService; PlacesService; TokenService; MyPostServiceListener; MyMeetingUIPlacesServiceListener; MyMeetingUITokenServiceListener; PostEvent; PlacesServiceEvent; TokenEvent; STWatchedUser; STUser; Post; ST_MEETING_TYPE_ERROR -1 ST_CHAT_MEETING0 ST_AUDIO_MEETING 1 ST_VIDEO_MEETING2 ST_COLLABORATION_MEETING3 ST_SHARE_MEETING4 ST_AV_WIZARD_MEETING 5 ST_HELP 6 CHAT_ACTIVITY AUDIO_ACTIVITY VIDEO_ACTIVITY APPSHARE_ACTIVITY WHITEBORD_ACTIVITY 1 2 4 8 16
#define #define #define #define #define #define #define #define #define #define #define #define #define
#define SAMETIME_MEETING_SERVER_PORT 8081 ////////////////////////// // MeetingUI - Sametime-enabled Chat/Audio/Video/Share/Collaborate Meetings using namespace std; class MeetingUI { friend class InviteUIDlg;
215
public: //Instace static MeetingUI* Instance(STSession* pSTSession = NULL); //Destruction void Destroy(); virtual ~MeetingUI(); // Api void CreateMeeting(list<STWatchedUser*> lstUsers ,long meetingType); void OpenAVHelp(); void OpenAVWizard(); bool IsMeetingsServiceAvailable(); //Services static CString FormatUrlCharacters(const CString &strInput); static long GetMeetingTypeFromActivities(long lActivities); static void OpenMRC(long lMeetingType, CString strUrl); wstring GetMyUserId(); CString GetServerName();
// Callback events from Post Service Listener void Posted(PostEvent); // Callback events from Places Service Listener void OnPlacesServiceAvailable(PlacesServiceEvent event); void OnPlacesServiceUnavailable(PlacesServiceEvent event); // Callback events from Token Service Listener void OnTokenGenerated(TokenEvent event); void OnGenerateTokenFailed(TokenEvent event); protected: //Calls from friend class InviteUIDlg void OnSend(Post* pPost, long lActivities, bool b1On1); void OnJoin(Post* pPost); void OnRespond(Post* pPost); private: //Private Constructor MeetingUI(STSession* pSTSession); //Private Methods CString GetMyUserName(); wstring GetUniquePlaceName(); void ArrangeActiveStatusOnly(list<STWatchedUser*>& lstUsers); static long GetRegKey(HKEY hKeyBase, LPCTSTR lpSubKey, LPTSTR lpszRetData);
216
CString GetAVWizardUrl(CString token); //Private members static MeetingUI* _Instance; static int m_refCount; STSession* m_pSTSession; PostService* m_pPostService; PlacesService* m_pPlacesService; TokenService* m_pTokenService; MyPostServiceListener* m_pMyPostServiceListener; MyMeetingUIPlacesServiceListener* m_pMyPlacesServiceListener; MyMeetingUITokenServiceListener* m_pMyTokenServiceListener; bool m_bServiceAvailable; }; #endif //__MEETING_UI__
Place
Places Service
Token Service
Post Service
Post
MyPlaceListener
MyMeetingControllerTokenServiceListener
MeetingController
217
Event handling
This class receives events from the Places Service listener, Place listener and the Token listener. In some cases the MeetingController class will handle these events by itself, in other cases they will be handled by the derived class, or even by both base and derived classes. For more detailed event handling, refer to Table 9-3.
Table 9-3 MeetingController event handling
The event OnTokenGenerated OnGenerateTokenFailed OnEntered OnLeft OnSectionAdded OnActivityRemoved OnActivityAdded OnEnterFailed OnAddActivityFailed NWayChatUIDlg, MeetingController MeetingController, MeetingLauncher MeetingController, MeetingLauncher The class/classes who handle the event MeetingController, MeetingLauncher MeetingController, MeetingLauncher NWayChatUIDlg, MeetingLauncher NWayChatUIDlg, MeetingLauncher NWayChatUIDlg
218
The class/classes who handle the event MeetingController NWayChatUIDlg, MeetingController NWayChatUIDlg, MeetingController
MeetingController.h
The MeetingController.h file is shown in Example 9-2.
Example 9-2 MeetingController.h
#if !defined __MEETING_CONTROLLER__ #define __MEETING_CONTROLLER__ #define #define #define #define #define CHAT_MRC_MEETING_ACTIVITY VIDEO_MRC_MEETING_ACTIVITY AUDIO_MRC_MEETING_ACTIVITY APPSHARE_MRC_MEETING_ACTIVITY WHITEBORD_MRC_MEETING_ACTIVITY 0x9106 0x9104 0x9103 0x9102 0x9101
#include "PlaceEvent.h" class class class class class class class class class class Place; TokenService; PlacesService; TokenEvent; PlacesServiceEvent; PlaceMemberEvent; MyPlaceListener; MyMeetingControllerTokenServiceListener; MyMeetingControllerPlacesServiceListener; STSession;
class MeetingController { public: //Constructors MeetingController(STSession* pSTSession, const Place& place, long lActivities, long lMeetingType, bool bInitiator = true);
//Destructor virtual ~MeetingController(); //Callback events from Token Service Listener virtual void OnTokenGenerated(TokenEvent event);
219
virtual void OnGenerateTokenFailed(TokenEvent event); //Callback events from Place Listener virtual void OnEntered(PlaceEvent event){}; virtual void OnLeft(PlaceEvent event){}; virtual void OnSectionAdded(PlaceEvent event){}; virtual void OnActivityRemoved(PlaceEvent event){}; virtual void OnActivityAdded(PlaceEvent event); virtual void OnEnterFailed(PlaceEvent event); virtual void OnAddActivityFailed(PlaceEvent event); virtual void OnAttributeChanged(PlaceMemberEvent event); //Callback events from Place Service Listener virtual void OnPlacesServiceAvailable(PlacesServiceEvent event); virtual void OnPlacesServiceUnavailable(PlacesServiceEvent event); protected: //Protected methods void AddMRCActivitiesToPlace(); //Protected Members Place* m_pPlace; long m_lActivities; long m_lMeetingType; bool m_bInitiator; void PrepareTheMRC(); bool m_bActivityAdded; private: //Private Methods CString GetFormattedActivities(); void SetActivitiesFromFormattedString(CString strActivities); CString PrepareMRCUrl(CString token); //Private Members TokenService* m_pTokenService; PlacesService* m_pPlacesService; MyPlaceListener* m_pPlaceListener; MyMeetingControllerTokenServiceListener* m_pTokenServiceListener; MyMeetingControllerPlacesServiceListener* m_pPlaceServiceListener; }; #endif //__MEETING_CONTROLLER__
220
MeetingController
CDialog MyUserInPlaceListener
NWayChatUIDlg
MyMyselfInPlaceListener
MySectionListener
Figure 9-10 on page 222 shows how the dialog appears and explains the different elements on the screen.
221
This Meeting menu provides more functions to the meeting like Add Tools.
Transcript window which shows all the messages sent on the meeting.
The meeting participant list shows all the users Who are currently in the meeting And their status.
This pane identify the incoming and outgoing of users from the meeting
The NWayChatUIDlg is a multi-user chat dialog. Unlike the ChatUIDlg, in which we can only talk to one person at a time, this new chat dialog gives us the ability to create a multiple-user chat room using the Places architecture, so if we send a message to the chat room everybody in the chat room will get the message and will be able to respond to it the same way. The NWayChatUIDlg can be upgraded to other meetings, like Audio, Video, Application sharing, and White board meetings by adding activities to the place. We use the AddToolsDlg to get the tools/activities that we want to add to the meeting, as shown in Figure 9-11 on page 223.
222
This class will use multiple inheritance, in which it will inherit both from the MeetingController and CDialog classes. Therefore, it can get all the Place and PlacesService events, will have the ability to upgrade to other meetings using the MeetingController class, and will get all the functionality of a dialog from the CDialog class. Refer to Figure 9-8 and Figure 9-9 for class diagrams. The NWayChatUIDlg will have a participant list to show who is in the chat room and the status of each participant. It will have a text box in which we can send messages to the chat room and a transcript window which records all the messages sent to the chat room. This is shown in Figure 9-10 on page 222.
223
Event handling
The NWayChatUIDlg class will handle all the basic events which are described in Table 9-3 on page 218. In addition, it will handle the enter/leave events of users to/from the chat room, along with enabling the sending and receipt of messages. For a full list of events handled by NWayChatUIDlg refer to Table 9-4.
Table 9-4 NWayChatUIDlg event handling
Handled by NWayChatUIDlg MeetingController NWayChatUIDlg MeetingController NWayChatUIDlg NWayChatUIDlg NWayChatUIDlg NWayChatUIDlg MeetingController NWayChatUIDlg NWayChatUIDlg NWayChatUIDlg NWayChatUIDlg NWayChatUIDlg The event OnPlacesServiceAvailable OnPlacesServiceUnavailable OnEntered OnLeft OnSectionAdded OnActivityAdded OnUsersEntered OnUserLeft OnTextReceived OnPlaceMemberAttributeChanged OnPlaceMemberAttributeRemoved Description Indicates when the Places Service is available. Indicates when the Places Service is not available. Indicates that we just entered the place. Indicates that we just left the place. A section has been added to the place. An activity has been added to the place. A user has just entered the section. A user has just left the section. Indicates that I have just received a text message. An attribute of a user has been changed. An attribute of a user was removed.
224
NWayChatUIDlg.h
The NWayChatUIDlg.h file is shown in Example 9-3.
Example 9-3 NWayChatUIDlg
#if !defined __NWAY_CHATUI_DLG__ #define __NWAY_CHATUI_DLG__ #pragma warning (disable : 4786) #include "resource.h" #include "MeetingController.h" #include <list> class class class class class class class STSsession; MySectionListener; MyselfInPlace; MyMyselfInPlaceListener; AwarenessList; MyUserInPlaceListener; AddToolsDlg;
class NWayChatUIDlg : public CDialog, public MeetingController { friend class AddToolsDlg; public: //Constructors NWayChatUIDlg(STSession* pSTSession, const Place& place, CString strMyName, bool bInitiator = true); //Destructor ~NWayChatUIDlg(); // Enums enum {IDD = IDD_NWAY_CHAT_DLG }; // Overrides MeetingController //PlacesService void OnPlacesServiceAvailable(PlacesServiceEvent event); void OnPlacesServiceUnavailable(PlacesServiceEvent event); //Place void OnEntered(PlaceEvent event); void OnLeft(PlaceEvent event); void OnSectionAdded(PlaceEvent event); void OnActivityAdded(PlaceEvent event); // Callback events from Section Listener
225
void OnUsersEntered(SectionEvent event); void OnUserLeft(SectionEvent event); // Callback events from MyselfInPlace Listener void OnTextReceived(MyselfEvent event); //Callback events from UserInPlace Listener void OnPlaceMemberAttributeChanged(PlaceMemberEvent event); void OnPlaceMemberAttributeRemoved(PlaceMemberEvent event); protected: //Callback events from AddToolsDlg void OnSendTools(long lActivities); void OnAddToolsDlgRequestClose(); // Overrides virtual void OnCancel(); virtual BOOL OnInitDialog(); virtual void DoDataExchange(CDataExchange* pDX); // Messages afx_msg void OnLeave(); afx_msg void OnSend(); afx_msg void OnChangeEditMessage(); afx_msg void OnAddTools(); afx_msg void OnClose(); DECLARE_MESSAGE_MAP() private: //Private members Section* m_pSection; MyselfInPlace* m_pMyselfInPlace; MySectionListener* m_pMySectionListener; MyMyselfInPlaceListener* m_pMyMyselfInPlaceListener; AwarenessList* m_pParticipantList; CString m_strTransScript; CString m_strMyName; CString m_strMessage; list<UserInPlace*> m_lstUsersInPlace; MyUserInPlaceListener* m_pMyUserInPlaceListener; AddToolsDlg* m_pAddToolsDlg; }; #endif //__NWAY_CHATUI_DLG__
226
AddToolsDlg.h
The AddToolsDlg.h file is shown in Example 9-4.
Example 9-4 AddToolsDlg.h
if !defined __ADD_TOOLS_DLG__ #define __ADD_TOOLS_DLG__ #include "resource.h" class NWayChatUIDlg; ////////////////////////// // AddToolsDlg class AddToolsDlg : public CDialog { public: //Constructor AddToolsDlg(NWayChatUIDlg* pCallBack, long lInitialActivities); // Destructor virtual ~AddToolsDlg(); // Enums enum { IDD = IDD_ADD_TOOLS}; protected: // Overrides virtual BOOL OnInitDialog(); virtual void OnCancel(); // Messages afx_msg void OnSend(); afx_msg void OnClose(); DECLARE_MESSAGE_MAP() private: // Private members NWayChatUIDlg* m_pCallBack; long m_lActivities; }; #endif // __ADD_TOOLS_DLG__
227
Event handling
The MeetingLauncher class will handle most of the MeetingController base class events. In some cases the MeetingLauncher will be the only class to handle the event and in other cases the MeetingLauncher will handle the event and pass it to the MeetingController to handle the event, too. For a general overview of MeetingController event handling, refer to Table 9-3 on page 218. For detailed event handling of the MeetingLauncher class, refer to Table 9-5.
Table 9-5 MeetingLauncher event handling
Handled by 1.MeetingLauncher 1.MeetingController 2.MeetingLauncher 1.MeetingController 2.MeetingLauncher The event OnEntered OnEnterFailed OnAddActivityFailed Description We just entered the place. We left the place. We have failed to add a new activity.
228
Description We left the place. A token has been generated for us. Generating the token failed.
MeetingLauncher.h
The MeetingLauncher.h file is shown in Example 9-1 on page 215
Example 9-5 MeetingLauncher.h
#if !defined __MEETING_LAUNCHER__ #define __MEETING_LAUNCHER__ #pragma warning (disable:4786) #include "resource.h" #include "MeetingController.h" class MeetingLauncher : public MeetingController { public: //Constructors MeetingLauncher(STSession* pSTSession, const Place& place, long lActivities, long lMeetingType, bool bInitiator = true); //Destructor ~MeetingLauncher(); // Overrides MeetingController //Place void OnEntered(PlaceEvent event); void OnEnterFailed(PlaceEvent event); void OnAddActivityFailed(PlaceEvent event); void OnLeft(PlaceEvent event); //Token Service void OnTokenGenerated(TokenEvent event); void OnGenerateTokenFailed(TokenEvent event); }; #endif //__MEETING_LAUNCHER__
229
CDialog
Post
InviteUIDlg
Click here to if you want to respond to the invitation this will open Im to the user who sent the Invitation.
For the incoming invitation we will have a dialog box which contains the topic of the invitation and the message itself. The dialog will also have an icon which identifies the meeting type (Chat, Audio, Video, Application Sharing, Collaborate), and 3 more buttons: Join, Respond and Close. Table 9-6 on page 231 describes the buttons in detail.
230
Change the invitation topic Change the invitation message Add and remove people from your participant list.
231
For the outgoing invitation dialog we have two text boxes: one to place the topic for the invitation and the second for the message of the invitation. We also have an invitees list, which represents all the users this invitation will be sent to. A meeting tools frame is used to specify the tools that we want in our meeting; we can add and remove tools while we are writing the invitation. Finally, we have two more buttons: Send, to send the invitation, and Cancel, to cancel the invitation.
Managing activities
Activities are the tools we want to add to the Sametime Meeting. In the Meetings sample we define the activities in two phases. The first phase is defined at the MeetingUIDlg.cpp and represents the activities as they are defined on the server side. The second phase is a local define of the activities, which will help us to add and remove activities. Refer to Table 9-9 for local activities definitions.
Table 9-9 Local activities definitions
Activity CHAT_ACTIVITY AUDIO_ACTIVITY VIDEO_ACTIVITY ID 1 2 4 Description A Chat activity. An Audio activity. A Video activity.
232
ID 8 16
As you can see in Table 9-9, every activity ID is represented by a bit in five bits number. Therefore, if you, for example, want to pass a number which represents both CHAT_ACTIVITY and AUDIO_ACTIVITY you must use bitwise operator and the result will be: CHAT_ACTIVITY | VIDEO_ACTIVITY = 5.
Note: Different combinations of activities represent different meeting types. Refer to Example 9-20 on page 253 to see how we convert activities to meeting types.
InviteUIDlg.h
The InviteUIDlg.h file is shown in Example 9-6.
Example 9-6 InviteUIDlg
#if !defined __INVITE_UI_DLG__ #define __INVITE_UI_DLG__ #pragma warning (disable: 4786) #include "resource.h" #include <list> class Post; class AwarenessList; class STWatchedUser; using namespace std;
233
class InviteUIDlg : public CDialog { public: // Static methods static void OpenInvitation(Post& post, long lmeetingType, const list<STWatchedUser*>& lstInvitedUsers, STSession* pSTsession); static void OpenInvited(Post& post, long lMeetingType); //Destructor ~InviteUIDlg(); // Enums enum { IDD_INVITATION = IDD_INVITEUI_INVITATION_DLG , IDD_INVITED = IDD_INVITEUI_INVITED_DLG}; protected: // Overrides virtual void OnCancel(); virtual BOOL OnInitDialog(); virtual void DoDataExchange(CDataExchange* pDX); // Messages afx_msg void afx_msg void afx_msg void afx_msg void
DECLARE_MESSAGE_MAP() private: //Private Constructors InviteUIDlg(Post& post ,long lmeetingType, const list<STWatchedUser*>* pLstInvitedUsers = NULL, STSession* pSTSession = NULL, bool bInvited = true); //Private functions void LoadInvitedInfoAndSettings(); void LoadInvitationInfoAndSettings(); CString GetMyUserName(); // Private members STSession* m_pSTSession; Post* m_pPost; AwarenessList* m_pInviteList; list<STWatchedUser*> m_lstInvitedUsers; CString m_strTopic; CString m_strMessage; CString m_strInviteText; bool m_bInvited; long m_lMeetingType;
234
}; #endif //__INVITE_UI_DLG__
CDialog
CHtmlView
MeetingDlg
MyHtmlView
The MeetingDlg class is a basic browser class which we will use to launch the Meeting Room Client (MRC), the Audio/Video wizard, and the Audio/Video help. This class will inherit from the CDialog class and will hold a private member of MyHtmlView class. MyHtmlView class inherits from CHtmlView Mfc class, and will be used to show the HTML content on the MeetingDlg dialog. Figure 9-16 on page 236 shows what the actual meeting dialog looks like.
235
236
We will initialize our class size and private members in the OnInitDialog member function. The MyHtmlView class member will also get its size and URL to be navigated in this member function.
Rectangle define
AUDIO_MEETING_DLG_RCT VIDEO_MEETING_DLG_RCT APPSHARE_MEETING_DLG_RCT COLLABORATE_MEETING_DLG_RCT AV_WIZARD_DLG_RCT HELP_DLG_RCT
Rectangle size
252x331 252x511 946x691 946x691 946x691 946x691
Navigating
To navigate to the URL that was provided by the constructor we simply use the Navigate2 member function of MyHtmView, which is an instance of the CHtmlView class. We call this member function after intializing the MyHtmlView private member in the OnInitDialog member function.
MeetingDlg.h
The MeetingDlg.h file is shown in Example 9-7.
Example 9-7 MeetingDlg.h
#if !defined __MEETING_DLG__ #define __MEETING_DLG__ #include "resource.h" #include <afxhtml.h> ////////////////////////// // MyHtmlView class MyHtmlView : public CHtmlView { public: MyHtmlView(){};
237
~MyHtmlView(){}; }; ////////////////////////// // MeetingDlg class MeetingDlg : public CDialog { public: //Constructor MeetingDlg(long lMeetingType, CString strUrl); // Destructor virtual ~MeetingDlg(); // Enums enum { IDD = IDD_MEETING_DLG}; protected: // Overrides virtual BOOL OnInitDialog(); virtual void OnCancel(); // Messages afx_msg void OnClose(); DECLARE_MESSAGE_MAP() private: //Private Methods CRect DlgRct2HtmlRct(CRect dlgRct); // Private members MyHtmlView* m_pHtmlView; CString m_strUrl; long m_lMeetingType; }; #endif // __MEETING_DLG__
238
In the MeetingUI constructor we initialize all Sametime class members and listeners, as you can see in Example 9-8. The way we free all the members memory is shown in Example 9-9. As you can see, we dont free the memory of services members because this will be done when we free the memory of the STSession class member which is in the MeetingsApp class.
Example 9-8 MeetingUI constructor
MeetingUI::MeetingUI(STSession* pSTSession) :m_pSTSession(pSTSession), m_pPostService(NULL), m_pPlacesService(NULL), m_pTokenService(NULL), m_pMyPlacesServiceListener(NULL), m_pMyPostServiceListener(NULL), m_pMyTokenServiceListener(NULL), m_bServiceAvailable(false) { if (!m_pSTSession) return; m_pPostService = (PostService*) m_pSTSession->getComponent(POST_COMP_NAME);
239
m_pPlacesService = (PlacesService*)m_pSTSession->getComponent(PLACES_COMP_NAME); m_pTokenService = (TokenService*) m_pSTSession->getComponent(TOKEN_COMP_NAME); if (!m_pPostService) m_pPostService = new PostService(m_pSTSession); if (!m_pPlacesService) m_pPlacesService = new PlacesService(m_pSTSession); if (!m_pTokenService) m_pTokenService = new TokenService(m_pSTSession); m_pMyPostServiceListener = new MyPostServiceListener(this); m_pMyPlacesServiceListener = new MyMeetingUIPlacesServiceListener(this); m_pMyTokenServiceListener = new MyMeetingUITokenServiceListener(this); if (m_pPostService && m_pMyPostServiceListener) m_pPostService->addPostServiceListener(m_pMyPostServiceListener); if (m_pPlacesService && m_pMyPlacesServiceListener) m_pPlacesService->addPlacesServiceListener(m_pMyPlacesServiceListener); if (m_pPostService) m_pPostService->registerPostType(INVITATION_POST_TYPE); }
240
To create Sametime Meetings, we just call the CreateMeeting API function from the AwarenessList.cpp. To create different Sametime Meetings, we just pass different Sametime Meeting Types (See Table 9-2 on page 214). The other parameters are the same. Refer to Example 9-11 to see how this is done in the AwarenessList class.
Example 9-11 Calling MeetingUI Api from AwarenessList.cpp
void AwarenessList::OpenChatWithSelectedUser() { int selectionCount = m_ctlAwarenessListView.GetSelectedCount(); if (selectionCount == 1 && m_pChatUI ) { STWatchedUser* pUser = GetSelectedUser(); m_pChatUI->Create1On1ChatById(*((STUser*)pUser)); return; } else { list<STWatchedUser*> lstUsers = GetSelectedUsers(); m_pMeetingUI->CreateMeeting(lstUsers,ST_CHAT_MEETING); } } . . . void AwarenessList::OnAudioPressed() { if (m_pMeetingUI) { m_pMeetingUI->CreateMeeting(GetSelectedUsers(),ST_AUDIO_MEETING);
241
} } void AwarenessList::OnVideoPressed() { if (m_pMeetingUI) { m_pMeetingUI->CreateMeeting(GetSelectedUsers(),ST_VIDEO_MEETING); } } void AwarenessList::OnSharePressed() { if (m_pMeetingUI) { m_pMeetingUI->CreateMeeting(GetSelectedUsers(),ST_SHARE_MEETING); } } void AwarenessList::OnCollaboratePressed() { if (m_pMeetingUI) { m_pMeetingUI->CreateMeeting(GetSelectedUsers(),ST_COLLABORATION_MEETING); } }
This concludes our description of the involved classes. We now look at the flow for creating the meetings.
242
Post Service
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
Places Service
Entering a section
Adding activities to the place Changing attributes of the place Attribute Added
Activities added
Token Service
Generate Token
Generating Token
Token generated
As you can see, there are two routes we can navigate to create a Sametime meeting. The first route is on the left side of the diagram and is intended to create a meeting when we are the initiators of the meeting. The second route is on the right side of the diagram and is intended to launch an existing meeting once we got the invitation from another user.
243
Figure 9-17 also illustrates the four steps we need to go though when we want to create/launch a meeting. Each of the first three steps in the diagram is related to a specific service in the Sametime C++ toolkit: Post, Places, and Token services. Step four in the diagram is related to the URL creation and browser launching, and completes the whole picture of creating a meeting.
244
245
For more information about using the InviteUIDlg class see The InviteUIDlg class on page 230.
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
Every invitation is based on a Post object we create or receive. In the Post object we put or get all the details of the invitation we create or recieve.
246
We call the CreateInvitation static function of the InviteUIDlg class to construct and initialize the InviteUIDlg class. We need to provide four parameters: Post object Post object will represent the new invitation and will be sent at the end of the process. Meeting Type The meeting type we want to invite the other users to. (For a list of meeting types refer to Table 9-2 on page 214.) Users List A list of users we want to send the invitation to. STSession The current session. Refer to Example 9-14 to see how its done in the MeetingUI.cpp when the CreateMeeting API function is called.
Example 9-14 Creating a new InviteUIDlg class
void MeetingUI::CreateMeeting(list<STWatchedUser*> lstUsers, long meetingType) { Post post = m_pPostService->createPost(INVITATION_POST_TYPE); InviteUIDlg::OpenInvitation(post, meetingType, lstUsers,m_pSTSession); }
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
247
248
; ;
||
||
||
||
||
249
Change the invitation topic Change the invitation message Add and remove people from your participant list.
Example 9-18 shows the code which will be executed when the Send button is pressed.
250
251
As you can see in Example 9-18, the first thing done after updating the dialog members is to find out what activities we would like to add to the meeting. This is done by checking each control to see if it is marked, and if so, we mark (by using bitwise) the specific bit in lActivities member which will represent all the activities we want to add to the meeting. After we find out what activities we want to add, we set the post object title and message as they appear in the invitation dialog. This is done by calling Post::setTitle and Post::setMessage. Finally, we want to tell the post object who are the users we want this post (Invitation) to be sent to, so we run through the participant list and add each user to the post object by using the AwarenessList::GetAllUsers and Post::addUser member functions. After we provide the post object all the relevant details (title, message, and users), we pass this information to the MeetingUI class to handle the Send request. Example 9-19 shows the code which is executed once the MeetingUI::OnSend member function is called.
Example 9-19 MeetingUI::OnSend member function
void MeetingUI::OnSend(Post* pPost, long lActivities, bool b1On1) { STStream dataPack; //Data Structure long lMeetingType = GetMeetingTypeFromActivities(lActivities); if (lMeetingType == ST_MEETING_TYPE_ERROR) return; unsigned short usEncryptionLevel = ST_ENC_LEVEL_RC2_40; wstring wstrPlaceName = GetUniquePlaceName(); wstring wstrPassword = L""; CString serverName = GetServerName(); BSTR bstr = serverName.AllocSysString(); wstring wstrServerName(bstr); bool bAutoInvite = false; unsigned long mark(0);
dataPack<<mark<<lMeetingType<<b1On1<<usEncryptionLevel<<wstrPlaceName<<wstrPass word<<wstrServerName<<bAutoInvite; bool bInviteByPlace(false); wstring strInvitorName(L""); wstring strUserId(L""); wstring strCommunityName(L""); long lPortOfSSL(0); dataPack<<bInviteByPlace<<strInvitorName<<strUserId<<strCommunityName<<lPortOfS SL<<mark; //Modify && send the post if (pPost)
252
Once the MeetingUI::OnSend member function is called by the InviteUIDlg class and passes the post, activities, and identifier parameters, the MeetingUI::OnSend member function goes through the following four steps: 1. Translate the activities member to a meeting type. 2. Build the data structure. 3. Send the invitation. 4. Create and enter the place. These steps are described in detail in the following sections.
253
else if ( lActivities & AUDIO_ACTIVITY && lActivities == AUDIO_ACTIVITY ) meetingType = ST_AUDIO_MEETING; else if ( lActivities & VIDEO_ACTIVITY && lActivities == VIDEO_ACTIVITY ) meetingType = ST_VIDEO_MEETING; else if ( lActivities & APPSHARE_ACTIVITY && lActivities == APPSHARE_ACTIVITY ) meetingType = ST_SHARE_MEETING; else if ( lActivities > 0) meetingType = ST_COLLABORATION_MEETING; return meetingType; }
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
The second thing MeetingUI::OnSend does is wrap some additional data into the data structure by using STStream class (See the Reference Guide for more details on STStream class) and set the post object with this data. The additional data we wrap using the STStream class is used to give the users who get the invitation some important data, like encryption level, server name, place name, and more. For details about how the data should be packed, refer to Table 9-12.
Important: The order in which the data is packed is important and should be as described in Table 9-12. Other data structures wont be accepted by standard Sametime clients and the invitation wont be accepted.
Table 9-12 Data structure to send with the invitation
No 1 2 Name Mark Meeting Type Type long (4 bytes) long (4 bytes) Description Marks the beginning of data structure - Must be 0. The meeting type we want to invite the other users to.
254
No 3 4 5 6 7 8 9 10 11 12
Name One On One Encryption Level Place Name Password Server Name Invited By Place Invitation Name User Id Community Name Port Of SSL
Type bool (1 byte) unsigned short (2 bytes) wstring wstring wstring bool wstring wstring wstring long (4 bytes)
Description Identify if this is 1On1 meeting or more users were invited. Identify the encryption level we want our meeting to have. Identify the place name where the meeting will take place. The place password if we want to protect the meeting with a password. The server name where the meeting will take place. For internal use. (Just put empty wstring.) For internal use. (Just put empty wstring.) For internal use. (Just put empty wstring.) For internal use. (Just put empty wstring.) The port of SSL when the meeting is created using SSL support, if not this value must be 0. Marks the end of data structure.
13
Mark
long (4 bytes)
After creating the data structure we use Post::setDetails to set this data to the post object and Post::setDetailsType to set the post data type.
255
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
Once the post object has all the data it needs, this post can finally be sent. This is done by calling Post::Send member function. Thats it; the Invitation was sent.
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
Accepting an invitation means that we have to listen for incoming posts. To listen for incoming posts we use the Post Service by adding a PostServiceListener listener and registering the specific post type we want the PostServiceListener to notify us with. In Example 9-12 on page 244 you can see how this is done in the MeetingUI constructor. Next we need to create a new PostServiceListener and add it to the PostService. Example 9-21 on page 257 shows how this is done in the MeetingUI constructor.
256
Once the PostServicelistener::posted member function is called, the MeetingUI::Posted member function is also called, as you can see in Example 9-22 and Example 9-23.
Example 9-22 MyPostServiceListener::posted member function
void MyPostServiceListener::posted(PostEvent event) { if (m_pCallBack) m_pCallBack->Posted(event); }
257
As you can see in Example 9-23, once the MeetingUI::Posted member function is called, the first thing we do is to check that the post is not one sent mistakenly by us. Next we check if the post type of the post is the valid post type we defined in Creating the invitation on page 246. Then we take out from the event the post object, the post type from the details, and finally, call the InviteUIDlg::OpenInvited static member function with all these parameters to open the Invitation dialog with the correct parameters.
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
Example 9-24 shows the code once the InviteUIDlg::OpenInvited static member function is called.
Example 9-24 InviteUIDlg::OpenInvited member function
void InviteUIDlg:: OpenInvited(Post& post, long lMeetingType) { InviteUIDlg* pDlg = new InviteUIDlg(post,lMeetingType); pDlg->Create(InviteUIDlg::IDD_INVITED); pDlg->ShowWindow(SW_SHOW); }
After creating a new InviteUIDlg dialog, InviteUIDlg constructor is called, as you can see in Example 9-16 on page 248. And once we called the InviteUIDlg::Create member function, the InviteUIDlg::OnInitDialog member function is called, as shown in Example 9-25.
258
Post Service
Accepting an invitation
Creating the invitation dialog Writing the data into the invitation Reading the data from the invitation
As you can see in Example 9-25, because its an Invited dialog, which means that we were invited to the meeting, the LoadInvitedInfoAndSettings member function is called to modify and update the dialog controls with the invitation data. This is shown in Example 9-26.
Example 9-26 InviteUIDlg::LoadInvitedInfoAndSettings member function
void InviteUIDlg::LoadInvitedInfoAndSettings() { STUserInstance sender; CString senderName = _T(""); CString invitationTitle = _T(""); CString invitationMessage = _T(""); CString topicMsg = _T(""); CString meetingTypeMsg = _T(""); int iMeetingIcon = 0; if (m_lMeetingType == ST_COLLABORATION_MEETING) { meetingTypeMsg = "Collaborate"; iMeetingIcon = IDI_COLLABORATE_MEETING_ICON; } else if (m_lMeetingType == ST_SHARE_MEETING)
259
{ meetingTypeMsg = "Application Sharing"; iMeetingIcon = IDI_SHARE_MEETING_ICON; } else if (m_lMeetingType == ST_VIDEO_MEETING) { meetingTypeMsg = "Video"; iMeetingIcon = IDI_VIDEO_MEETING_ICON; } else if (m_lMeetingType == ST_AUDIO_MEETING) { meetingTypeMsg = "Audio"; iMeetingIcon = IDI_AUDIO_MEETING_ICON; } else if (m_lMeetingType == ST_CHAT_MEETING) { meetingTypeMsg = "Chat"; iMeetingIcon = IDI_CHAT_MEETING_ICON; } if (iMeetingIcon != 0) { HICON hIcon = AfxGetApp()->LoadIcon(iMeetingIcon); if (hIcon) ((CStatic*)(GetDlgItem(IDC_MEETING_TYPE_ICON)))->SetIcon(hIcon); } if (m_pPost) { sender = m_pPost->getSenderDetails(); senderName = sender.GetName().c_str(); invitationTitle = m_pPost->getTitle().c_str(); invitationMessage = m_pPost->getMessage().c_str(); topicMsg.Format("%s is inviting you to a %s meeting",senderName,meetingTypeMsg); m_strInviteText.Format("Topic: %s\r\nMessage: %s",invitationTitle,invitationMessage); } GetDlgItem(IDC_YOU_ARE_INVITED)->SetWindowText(topicMsg); UpdateData(FALSE); }
In Example 9-26 we set the dialog meeting type, topic, and message fields according to the data we get from the post object, which is the invitation data. Once the dialog appears, we will see all the invitation data as it was sent. This is shown in Figure 9-27.
260
Click here to if you want to respond to the invitation this will open Im to the user who sent the Invitation.
Once the dialog appears, the user has three choices: accept the invitation and Join the meeting, Respond to the invitation by opening an Im to the user who sent the invitation (this action wont close the dialog), or Close to decline the invitation and close the dialog.
Join
Once the user chooses to join the meeting, the InviteUIDlg::OnJoin member function is called. This is shown in Example 9-27.
Example 9-27 InviteUIDlg::OnJoin member function
void InviteUIDlg::OnJoin() { MeetingUI* pMeetingUI = MeetingUI::Instance(); if (pMeetingUI) { pMeetingUI->OnJoin(m_pPost); pMeetingUI->Destroy(); } DestroyWindow(); delete this; }
As you can see in Example 9-27, the InviteUIDlg::OnJoin calls the MeetingUI::OnJoin, passing him the post parameter to handle the request of joining the meeting. This is shown in Example 9-28 on page 262.
261
262
Example 9-28 shows how the MeetingUI::OnJoin member function first extracts all the data from the post object so we know to what place we are to join, what the password is, and so on. This data structure is similar to the data structure described on Table 9-12 on page 254. You must pull the data out the same way you put it in when you created the invitation. Next, the MeetingUI::OnJoin member function joins the place based on the data it gets from the post object. For more details on joining a place see 9.4.2, The place on page 264.
Respond
Once the user clicks the Respond button, the InviteUIDlg::OnRespond member function is called. InviteUIDlg::OnRespond calls the MeetingUI::OnRespond member function and passes it the post object to handle the request, as you can see in Example 9-29.
Example 9-29 InviteUIDlg::OnRespond member function
void InviteUIDlg::OnRespond() { MeetingUI* pMeetingUI = MeetingUI::Instance(); if (pMeetingUI) { pMeetingUI->OnRespond(m_pPost); pMeetingUI->Destroy(); } }
Once the MeetingUI::OnRespond member function is called, it uses the ChatUI::Create1On1ChatById member function to create Chat with the user who sent the post. This is shown in Example 9-30. For more information about the ChatUI class, refer to the ExtLiveNames sample in the C++ toolkit tutorial.
Example 9-30 MeetingUI::OnRespond member function
void MeetingUI::OnRespond(Post* pPost) { ChatUI* pChatUI = ChatUI::Instance(); if (pChatUI && pPost) { STUserInstance user = pPost->getSenderDetails(); STUser* pUser = (STUser*)&user; pChatUI->Create1On1ChatById(*pUser); pChatUI->Destroy(); } }
263
Once the chat dialog appears, the user can ask questions about the invitation directed to the user who sent the invitation. When the user is finished responding to the invitation, he still can decide to Join or to Close the invitation.
Close
If the user chooses to close the invitation, the InviteUIDlg is destroyed, along with its members, including the post object, which means that this user declined to join the meeting.
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
We will use the PlaceService::CreatePlace member function to create a place, providing it with the following parameters: Place Name The unique place name we want our place to have (see Example 9-19 on page 252 and Example 9-31 on page 265).
264
Place Display Name The display name we want to provide for the place, this name can be displayed, for example, as the title of the window. Encryption Level The encryption level we want our place to have. See Table 9-13 on page 266 for a list of all Encryption Levels IDs. Place Type The Place type we want our place to have. In our case this type is 0 and is defined as SAMETIME_PLACE_TYPE = 0. Publish Mode The publish mode we want our place to have, for example if we want our place to be a public place or not. See Table 9-14 on page 266 for a list of all publish modes. The following code shows how we do this in the Meetings Sample on MeetingUI::OnSend at the time we have all the information we need from the invitation. (Refer to Example 9-19 on page 252 for the entire code.)
. . . Place place = m_pPlacesService->createPlace(wstrPlaceName, pPost ? pPost->getTitle() : L"", STEncLevel(usEncryptionLevel), SAMETIME_PLACE_TYPE, PlacesConstants::PLACE_PUBLISH_MODE_NOT_PUBLISHED); . . .
Example 9-31 Generating a unique place name using the MeetingUI::GetUniquePlaceName member function
wstring MeetingUI::GetUniquePlaceName() { static long id; CString strId; time_t t = CTime::GetCurrentTime().GetTime(); strId.Format("%S%ld%ld",GetMyUserId().c_str(),++id*rand(),t); BSTR bstrId = strId.AllocSysString(); wstring wstrId(bstrId); ::SysFreeString(bstrId); return wstrId; }
265
Places Service
Entering a section Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
266
Joining an existing place means that when we enter the place we will enter the place in join mode. The place object is created the same as if we created a new place. The main difference is that here we get all the parameters for creating the place from the invitation we received from the user who invited us. Refer to Example 9-28 on page 262 to see how this was done in the MeetingUI::OnJoin member function when we decided to join the place. Also refer to the next section to see the difference between creating and joining a place.
PLACE_CREATION_MODE_JOIN PLACE_CREATION_MODE_DONT_CARE
1 2
In Example 9-32 you can see how we enter a place in the MeetingController constructor (For more details about MeetingController class, refer to 9.2.2, The MeetingController class on page 217).
Example 9-32 MeetingController Constructor
MeetingController::MeetingController(STSession* pSTSession, const Place& place, long lActivities, long lMeetingType, bool bInitiator) :m_pPlace(new Place(place)), m_pPlaceListener(NULL), m_pTokenService(NULL), m_pPlacesService(NULL), m_pTokenServiceListener(NULL), m_pPlaceServiceListener(NULL), m_lActivities(lActivities), m_lMeetingType(lMeetingType), m_bInitiator(bInitiator), m_bActivityAdded(false) { m_pPlaceListener = new MyPlaceListener(this); if (m_pPlace && m_pPlaceListener) {
267
m_pPlace->addPlaceListener(m_pPlaceListener); if (m_bInitiator) m_pPlace->enter(L"",PlacesConstants::PLACE_CREATION_MODE_CREATE,true); else m_pPlace->enter(L"",PlacesConstants::PLACE_CREATION_MODE_JOIN,true); } if (pSTSession) { m_pTokenService = (TokenService*)(pSTSession->getComponent(TOKEN_COMP_NAME)); if (!m_pTokenService) m_pTokenService = new TokenService(pSTSession); m_pPlacesService = (PlacesService*)(pSTSession->getComponent(PLACES_COMP_NAME)); if (!m_pPlacesService) m_pPlacesService = new PlacesService(pSTSession); m_pPlaceServiceListener = new MyMeetingControllerPlacesServiceListener(this); m_pPlacesService->addPlacesServiceListener(m_pPlaceServiceListener); } m_pTokenServiceListener = new MyMeetingControllerTokenServiceListener(this); }
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
268
We create the NWay-Chat Dialog when the meeting type we get from the invitation is chat. There are two cases when we will create the NWay-Chat Dilaog: when we invite other users to an NWayChat meeting, and when we were invited to a chat meeting. We create the NWayChatUIDlg with the same parameters in both cases, with only one difference. This difference is the initiator parameter, which is true if we are the initiators of the place and false otherwise. Refer to 9.2.3, The NWayChatUIDlg class on page 221 for an overview of the NWayChatUIDlg class, and to 9.2.2, The MeetingController class on page 217 for an overview on the MeetingController class. Example 9-19 on page 252 and Example 9-28 on page 262 show how the NWayChatUIDlg is created in each one of the cases. When the NWayChatUIDlg constructor is launched, it calls its parent class constructor, which is the MeetingController constructor. You can see this in Example 9-33.
Example 9-33 NWayChatUIDlg Constructor
NWayChatUIDlg::NWayChatUIDlg(STSession* pSTSession, const Place& place, CString strMyName, bool bInitiator) :CDialog(NWayChatUIDlg::IDD, NULL), MeetingController(pSTSession,place,CHAT_ACTIVITY,ST_CHAT_MEETING, bInitiator), m_pSection(NULL), m_pMyselfInPlace(NULL), m_pMySectionListener(NULL), m_pMyMyselfInPlaceListener(NULL), m_pParticipantList(NULL), m_pMyUserInPlaceListener(NULL), m_strMyName(strMyName), m_pAddToolsDlg(NULL) { }
Once the MeetingController constructor has been called, we create a place listener and add it to the place object, which is kept as a member in the MeetingController class. Next we add a PlaceService listener to the place service handle, which we also have as a member in the MeetingController class. See Example 9-32 on page 267 to see how its done. Once we passed both NWayChatUIDlg and MeetingController constructors and we sent the place object a request to enter the place, we have to wait for the Entered event and then the MyPlaceListener::sectionAdded events to continue with the process. Once the MyPlaceListener::entered member function has been called, the NWayChatUIDl::OnEnterd member function is called, as you can see in Example 9-34 and Example 9-35 on page 270.
269
Now look at Example 9-35. Once the NWayChatUIDlg::OnEntered has been called, we call the Create and ShowWindow member functions to create the dialog. This is done here because we dont want our dialog to be displayed before we are sure we entered the place.
Example 9-35 NWayChatUIDlg::OnEntered member function
void NWayChatUIDlg::OnEntered(PlaceEvent event) { Create(IDD_NWAY_CHAT_DLG); ShowWindow(SW_SHOW); BringWindowToTop(); if (m_hWnd) SetWindowText(CString(event.getPlace().getDisplayName().c_str())); if (!m_pMyUserInPlaceListener) m_pMyUserInPlaceListener = new MyUserInPlaceListener(this); }
There is also the possibility that we will fail entering the place. In this case we would like to use the default behavior in the MeetingController base class so we wont override this member function (For more information about event handling between these two classes, refer to Table 9-4 on page 224.) Example 9-36 and Example 9-37 show how we handle this event.
Example 9-36 MyPlaceListener::enteredFailed member function
void MyPlaceListener::enterFailed(PlaceEvent event) { if (m_pCallBack) m_pCallBack->OnEnterFailed(event); }
270
After calling the NWayChatUIDlg::Create member function in the NWayChatUIDlg::OnEnterd, we expect the NWayChatUIDlg::OnInitDialog to be called. Once this is done, as you can see in Example 9-38, we create the NWayChatUIDlg participant list, which is an AwarenessList class that gives us the support for live names. (Refer to the Tutorial start from the LiveNames sample for more information about the AwarenessList class.)
Example 9-38 NWayChatUIDlg::OnInitDialog member function
BOOL NWayChatUIDlg::OnInitDialog() { CDialog::OnInitDialog(); if (m_pPlace && m_pPlace->getSession()) { CRect rct; GetDlgItem(IDC_STATIC_FOR_AWARENESS_LIST)->GetWindowRect(&rct); ScreenToClient(&rct); m_pParticipantList = new AwarenessList(m_pPlace->getSession(), rct,this,AL_WRITING_NOTIFY); m_pParticipantList->Create(IDD_AWARENESS_LIST, this); m_pParticipantList->ShowMenu(TRUE); m_pParticipantList->EnableRemoving(FALSE); m_pParticipantList->ShowWindow(SW_SHOW); } return TRUE; }
271
Entering A Section
Creating The Place Joining The Place
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
When we called the Place::enterPlace member function, we passed several parameters, the last of which was if we want to go to the stage section. In all cases we enter the stage section. (For more information about sections in a place refer to 3.3.2, Sections on page 53). As we mentioned before, there are actually two events we are waiting for to continue with the process. One is the MyPlaceListenerEvent::entered, which we already handled, and the other is the MyPlaceListener::sectionAdded event. Once the MyPlaceListener::sectionAdded member function has been called, the NWayChatUIDlg::OnSectionAdded member function is called, as you can see in Example 9-39 and Example 9-40.
Example 9-39 MyPlaceListener::sectionAdded member function
void MyPlaceListener::sectionAdded(PlaceEvent event) { if (m_pCallBack) m_pCallBack->OnSectionAdded(event); }
272
Look at Example 9-40. The only section we are waiting for is the stage section, so with any event that carries section which is not the stage section, we return without doing anything. Once we get the stage section, we create a new section using the copy constructor of the section. After doing this, we add to this section a new section listener so we will get notifications from the section, like user entered and user left events, which are described in more details in the next chapter. The NWay-Chat dialog that appears will look like Figure 9-32 (except that we have added annotations here to describe its functionality).
This Meeting menu provides more functions to the meeting like Add Tools.
Transcript window which shows all the messages sent on the meeting.
The meeting participant list shows all the users Who are currently in the meeting And their status.
This pane identify the incoming and outgoing of users from the meeting
273
274
m_pParticipantList->AddUser(*itr); if (m_hWnd && typeid(**itr) != typeid(MyselfInPlace)) { CString strUserName; strUserName.Format("%S joined the meeting.",(*itr)->GetName().c_str()); GetDlgItem(IDC_STATIC_STATUS)->SetWindowText(strUserName); } } } }
275
As you can see in Example 9-42 and Example 9-44, we use these notifications to change the text in the lower pane, which identifies the movements of users.
Chatting
Joining The Place Creating The Place
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
To send text to the other users who are currently in the same section we just use the Section::sendText member function. Example 9-45 shows how this is done in the NWayCharUIDlg::OnSend member function.
Example 9-45 Sending text to the section on NWayChatUIDlg::OnSend member function
void NWayChatUIDlg::OnSend() { UpdateData(); BSTR bstr = m_strMessage.AllocSysString(); if (m_pSection) m_pSection->sendText(bstr); m_strMessage = ""; ::SysFreeString(bstr); UpdateData(FALSE); OnChangeEditMessage(); }
To receive Text which is sent to the section, we use the MyselfInPlaceListener, which will notify the NWayChatUIDlg class. Example 9-46 and Example 9-47 show how this is done.
Example 9-46 MyMyselfInPlaceListener::textReceived member function
void MyMyselfInPlaceListener::textReceived(MyselfEvent event) {
276
if (m_pCallBack) m_pCallBack->TextReceived(event); }
As you can see in Example 9-47, we call the MyselfEvent::getSender member function to get the user name of the user who sent the text so we can place it on the transcript window next to the text he sent.
void NWayChatUIDlg::OnChangeEditMessage() { CString str; GetDlgItem(IDC_EDIT_MESSAGE)->GetWindowText(str); if (m_pMyselfInPlace && str.GetLength() == 1) { STExtendedAttribute attribute(AwarenessList::USER_START_STOP_WRITING); m_pMyselfInPlace->changeAttribute(&attribute); }
277
278
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
Adding tools to an existing chat meeting is an option that any user in a chat meeting can use. To open the Add Tools dialog from the NWay-Chat dialog, go to the Meeting menu and select the Add Tools option, as you can see in Figure 9-35.
279
Note: The Add Tools dialog is a UI code-only class, so we will not describe it in much detail. If you want to see the full source code, you can download it from the IBM Redbooks Web site. See Appendix E, Additional Web material on page 407 for instructions.
Once the user clicks the Send button, the NWayChatUIDlg::OnSendTools member function is called, as you can see in Example 9-51.
Example 9-51 NWayChatUIDlg::OnSendTools member function
void NWayChatUIDlg::OnSendTools(long lActivities) { m_lActivities = lActivities; if (lActivities > CHAT_ACTIVITY) { m_lMeetingType = MeetingUI::GetMeetingTypeFromActivities(m_lActivities); PrepareTheMRC(); AddMRCActivitiesToPlace(); } }
As you can see in this example, the first thing we do is to translate the activities to a meeting type, as we did in Creating the invitation on page 246, so we can upgrade the meeting to a specific meeting type according to the tools we added. The second thing we do is call the MeetingController::AddMRCActivitiesToPlace member function, which is discussed in more detail in the next section.
280
Places Service
Entering a section Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
One of the basic steps on the way to creating Sametime Meetings is adding activities to the place. The activities we add to the place are determined by what type of meeting we would like to have, or more precisely, by what the activities in this meeting will be. Note that the activities we want to add to the place must be supported by the server. For a general description of activities in a place refer to 3.3.3, Activities on page 53. Refer to Table 9-10 to see all Sametime Meeting Room Client (MRC) activities types. Example 9-52 illustrates how we add them to the place in the MeetingController::AddMRCActivitiesToPlace member function according to the activities the user wants to add to the meeting.
Example 9-52 MeetingController::AddMRCActivitiesToPlace member function
void MeetingController::AddMRCActivitiesToPlace() { if (!m_pPlace) return; if (m_lActivities & CHAT_ACTIVITY) m_pPlace->addActivity(CHAT_MRC_MEETING_ACTIVITY,STStream()); if (m_lActivities & AUDIO_ACTIVITY) m_pPlace->addActivity(AUDIO_MRC_MEETING_ACTIVITY,STStream()); if (m_lActivities & VIDEO_ACTIVITY) m_pPlace->addActivity(VIDEO_MRC_MEETING_ACTIVITY,STStream()); if (m_lActivities & APPSHARE_ACTIVITY) m_pPlace->addActivity(APPSHARE_MRC_MEETING_ACTIVITY,STStream());
281
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
Another step in creating Sametime Meetings is to change some place attributes. The MeetingController::PrepareTheMRC member function does all the attribute changes we need before we launch a Sametime Meeting, as shown in Example 9-53. The available MRC attribute IDs are identified in Table 9-16 on page 283.
Example 9-53 Changing place attributes - MeetingController::PrepareTheMRC member function
void MeetingController::PrepareTheMRC() { STStream dataPack; // Set the meeting kind dataPack<<MEETING_MODE_INSTANT; STExtendedAttribute attrMeeting(MEETING_STARTUP_MODE_ID,dataPack); if (m_pPlace) m_pPlace->changeAttribute(&attrMeeting); // Set the requested activities CString str = GetFormattedActivities(); BSTR bstr = str.AllocSysString(); wstring strAct(bstr);
282
dataPack.clear(); dataPack<<strAct; STExtendedAttribute attrRequest(MEETING_REQUEST_ACTIVITIES_ID,dataPack); if (m_pPlace) m_pPlace->changeAttribute(&attrRequest); ::SysFreeString(bstr); // Set the moderator ID MeetingUI* pMeetingUI = MeetingUI::Instance(); if (pMeetingUI) { wstring id = pMeetingUI->GetMyUserId(); wstring community(L""); dataPack.clear(); dataPack<<id<<community; STExtendedAttribute attrModerator(MEETING_MODEARATOR_ID,dataPack); if (m_pPlace) m_pPlace->changeAttribute(&attrModerator); } // Set the meeting time CTime time = CTime::GetCurrentTime(); time_t t = time.GetTime(); dataPack.clear(); dataPack<<t; STExtendedAttribute attrTime(MEETING_STARTING_TIME_ID,dataPack); if (m_pPlace) m_pPlace->changeAttribute(&attrTime); }
283
As you can see in Example 9-53, the MeetingController::PrepareTheMRC member function uses the MeetingController::GetFormattedActivities member function to translate all the required activities to a formatted string, so it can be set as an attribute to the place and the Meeting Room Client can read it. See Example 9-54.
Example 9-54 MeetingController::GetFormattedActivities member function
CString MeetingController::GetFormattedActivities() { CString tmp(""),strActivities(""); if (m_lActivities & CHAT_ACTIVITY) { tmp.Format("%ld,",CHAT_MRC_MEETING_ACTIVITY); strActivities +=tmp; } if (m_lActivities & AUDIO_ACTIVITY) { tmp.Format("%ld,",AUDIO_MRC_MEETING_ACTIVITY); strActivities +=tmp; } if (m_lActivities & VIDEO_ACTIVITY) { tmp.Format("%ld,",VIDEO_MRC_MEETING_ACTIVITY); strActivities +=tmp; } if (m_lActivities & APPSHARE_ACTIVITY) { tmp.Format("%ld,",APPSHARE_MRC_MEETING_ACTIVITY); strActivities +=tmp; } if (m_lActivities & WHITEBORD_ACTIVITY) { tmp.Format("%ld,",WHITEBORD_MRC_MEETING_ACTIVITY); strActivities +=tmp; } // Remove the last comma strActivities = strActivities.Left(strActivities.GetLength() - 1 ); return strActivities; }
284
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
We now create the meeting launcher that the user needs to launch a non-chat meeting. Refer to Example 9-19 on page 252 to see how we created the class with an outgoing invitation, and to Example 9-28 on page 262 for an incoming invitation. Once the MeetingLauncher constructor has been called, it passes all its parameters to the MeetingController base class constructor, as shown in Example 9-55. You can also refer to Example 9-32 on page 267 to see the MeetingController constructor once it has been called by the MeetingLauncher constructor. Actually, the MeetingController will do most of the work for the MeetingLauncher class, so most of the things we already mentioned about the MeetingController class wont be mentioned again. Refer to 9.2.4, The MeetingLauncher class on page 228 to get a general overview about this class and to Table 9-3 on page 218 for a description of event handling between the MeetingController class and the MeetingLauncher class.
Example 9-55 MeetingLauncher Constructor
MeetingLauncher::MeetingLauncher(STSession* pSTSession, const Place& place, long lActivities, long lMeetingType, bool bInitiator) :MeetingController(pSTSession,place,lActivities,lMeetingType,bInitiator) { }
285
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
Once we have passed both the MeetingController and the MeetingLauncher constructors, we have to wait for the PlaceListener::entered member function to be called. Once it has been called, it calls the MeetingLauncher::OnEntered member function shown in Example 9-34 on page 270 and in Example 9-56.
Example 9-56 MeetingLauncher::OnEntered member function
void MeetingLauncher::OnEntered(PlaceEvent event) { if (m_bInitiator) { PrepareTheMRC(); AddMRCActivitiesToPlace(); } }
There is also another option: we will fail entering the place. This is handled by both the MeetingController and MeetingLauncher classes, as shown in Example 9-37 on page 271 and in Example 9-57.
Example 9-57 MeetingLauncher::OnEnterFailed member function
void MeetingLauncher::OnEnterFailed(PlaceEvent event) { MeetingController::OnEnterFailed(event); delete this; }
286
As Example 9-56 shows, after checking if we are the initiators of the meeting we call the MeetingController::PrepareTheMRC and MeetingController::AddMRCActivitiesToPlace member functions to prepare the place for the meeting. Refer to Example 9-52 on page 281 to see the code which adds the activities to the place, and to Example 9-53 on page 282 to see how we change the place attributes in the MeetingController::PrepareTheMRC member function.
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
Once we have changed the place attribute, we wait for the AttributeChanged event. This is done in the MyPlaceListener::attributeChanged member function; once it has been called, it calls the MeetingController::OnAttributeChanged member function, as shown in Example 9-58 and Example 9-59.
Example 9-58 MyPlaceListener::attributeChanged member function
void MyPlaceListener::attributeChanged(PlaceMemberEvent event) { if (m_pCallBack) m_pCallBack->OnAttributeChanged(event); }
287
{ STAttribute* pAttribute = event.getAttribute(); if (pAttribute) SetActivitiesFromFormattedString(pAttribute->getString().c_str()); if (m_bActivityAdded && m_pTokenService && m_pTokenServiceListener) { m_pTokenService->addTokenListener(m_pTokenServiceListener); m_pTokenService->generateToken(); } } }
As you can see in Example 9-59, we are waiting for a specific attribute ID, MEETING_REQUEST_ACTIVITIES_ID, which is the activities who were formatted into a string by the initiator of the meeting. (See Adding activities and attributes to the place on page 286.) In this way the other users who join the meeting know what activities have been added to the place. We use the MeetingController::SetActivitiesFromFormattedString member function to generate all the activities to a long type from the formatted string. This data is important when we need to create a URL that will launch the Sametime Meeting. This is discussed in more detail in Launch Sametime meeting on page 292. Once we do this, we add the token listener to the token object and generate the token, which is discussed in more detail in 9.4.3, Generating a token on page 290.
Places Service
Entering a section
Chatting Adding tools Adding activities to the place Changing attributes of the place Activities added Attribute Added
288
Once we have added the activities to the place, we wait for the ActivitiesAdded event to occur. Once the MyPlaceListener::activityAdded member function has been called, it calls the MeetingController::OnActivityAdded member function, as you can see in Example 9-60 and Example 9-61.
Example 9-60 MyPlaceListener::activityAdded member function
void MyPlaceListener::activityAdded(PlaceEvent event) { if (m_pCallBack) m_pCallBack->OnActivityAdded(event); }
There is also the possibility that our request to add activity will fail. We handle this in the MeetingController::OnAddActivityFailed member function, as you can see in Example 9-62.
Example 9-62 MeetingController::OnActivityFailed member function
void MeetingController::OnAddActivityFailed(PlaceEvent event) { long reason = event.getReason(); CString strMsg; strMsg.Format("Add Activity Failed. reason : 0x%x",reason); ::MessageBox(NULL,strMsg, "Sametime", MB_OK | MB_ICONEXCLAMATION); }
289
As you can see in Example 9-61, after checking if the activity that was added is a known activity, we add a token listener and generate a token. This step is discussed in detail in the next section.
Token generated
Generating a token is very simple. All we have to do is add a TokenListener to our TokenService handler and call the TokenService::generateToken member function. As you can see in Example 9-59 on page 287, we created a new TokenListener and added it to the TokenService, and then called the TokenService::generateToken.
Token Service
Generate Token
Now we wait for the token to be generated by the TokenService. Once this has been done, we handle this event in both MeetingLauncher and MeetingController classes. Refer to Example 9-63, Example 9-64, and Example 9-65 to see how its done.
Generate Token
Generating Token
Token generated
290
There is also the possibility that the Token Service will fail to generate the token. This event is handled also by both MeetingLauncher and MeetingController classes, as you can see in Example 9-66 and Example 9-67.
Example 9-66 MeetingLauncher::OnGenerateTokenFailed member function
void MeetingLauncher::OnGenerateTokenFailed(TokenEvent event) { MeetingController::OnGenerateTokenFailed(event); delete this; }
291
As you can see in Example 9-65, the first thing we do when the MeetingController::OnTokenGenerated member function has been called is remove the TokenListener from the TokenService so we wont get any events from the TokenService. Next we call the MeetingUI::GetMeetingTypeFromActivities member function so we can know what type of meeting it is going to be. This will help us open the browser window to the correct size. The third thing we do is call the MeetingController::PrepareMRCUrl and MeetingUI::OpenMRC member functions, which we discuss in more detail in the next section. The last thing we do is simply leave the place.
There are actually two member functions we need to use to create the URL: MeetingController::PrepareMRCUrl and MeetingUI::FormatUrlCharacters. These two functions will help you create the URL you need to open the browser. Refer to Example 9-68 for the code.
Example 9-68 MeetingController::PrepareMRCURL member function
CString MeetingController::PrepareMRCUrl(CString token) {
292
CString strUrl; // Get the user name from the community MeetingUI* pMeetingUI = MeetingUI::Instance(); if (!pMeetingUI) return ""; // Build the URL strUrl = "http://"; strUrl += pMeetingUI->GetServerName(); strUrl += "/stsrc.nsf/join?OpenForm"; strUrl += "&mid="; strUrl += m_pPlace ? pMeetingUI->FormatUrlCharacters(CString(m_pPlace->getPlaceName().c_str())) : ""; strUrl += "&toktype=1&tok="; strUrl += token; strUrl += "&port="; CString sTemp; sTemp.Format("%i", SAMETIME_MEETING_SERVER_PORT); strUrl += sTemp; strUrl += "&ismgr="; strUrl += (m_bInitiator ? "1" : "0"); strUrl += "&uname="; strUrl += pMeetingUI->FormatUrlCharacters(CString(pMeetingUI->GetMyUserId().c_str())); return strUrl; }
293
|| ((character >= 'a') && (character <= 'z')) || ((character >= '0') && (character <= '9'))) { strRetVal += character; } else { tmp.Format("%c%02X%c%02X%d%c%02X", '%', '&', '%', '#', character, '%', ';'); strRetVal += tmp; } } ::SysFreeString(b); return strRetVal; }
Once we have created the URL, the only thing left to do is to launch the browser. We do this by calling the MeetingUI::OpenMRC static member function by passing it the meeting type and the URL, as shown in Example 9-70. As you can see in Example 9-71 and Example 9-72, the MeetingUI::OpenMRC member function will create the browser window and navigate to the URL we supplied.
Example 9-70 MeetingController::OnTokenGenerated - Calling MeetingUI::OpenMRC to open the browser window.
void MeetingController::OnTokenGenerated(TokenEvent event) { if (m_pTokenService && m_pTokenServiceListener) m_pTokenService->removeTokenListener(m_pTokenServiceListener); CString tokenString = CString(event.getToken().getTokenString().c_str()); if (! m_lMeetingType || m_lMeetingType == ST_CHAT_MEETING) m_lMeetingType = MeetingUI::GetMeetingTypeFromActivities(m_lActivities); CString strUrl = PrepareMRCUrl(tokenString); MeetingUI::OpenMRC(m_lMeetingType, strUrl);
294
if (m_pPlace) m_pPlace->leave(ST_OK); }
295
case ST_HELP: MoveWindow(HELP_DLG_RCT); m_pHtmlView->MoveWindow(DlgRct2HtmlRct(HELP_DLG_RCT)); SetWindowText("Sametime Help"); break; } m_pHtmlView->Navigate2(m_strUrl); return TRUE; }
9.5 Summary
In this chapter we have walked you through a full-featured Sametime C++ program. You have had the opportunity to learn a great deal about developing with C++ for Sametime. You have also been able to see how a real-world program can leverage the Sametime places architecture.
296
10
Chapter 10.
297
Win32 versus MFC: If you dont know (or dont care) what the difference is between writing Win32 and MFC code you can go right on to the next chapter.
Basically, Microsoft Foundation Classes supply the programmer with a framework that in some cases offers generalization at the cost of execution speed. Win32 does not supply any abstraction layers, which some programmers claim makes it faster than MFC. There are about 200 MFC classes, in contrast to more than 2000 Win32 API calls. The truth about the real difference between Win32 and MFC is out there somewhere, but it is not our objective to answer that question in this book. The Sametime C++ toolkit works with both, so you can use what you like.
Once this sample is called with the above parameters, it will log into the specific host-name using the user-name and password which is provided. Once it receives the loggedIn event, it will change the users-status using the parameters provided. Once the myStatusChanged event is received, the sample will log out from the server and close itself. See Figure 10-1 for an example command line.
298
//Listeners class MyLoginListener : public LoginListener { public: virtual void loggedIn(LoginEvent event); virtual void loggedOut(LoginEvent event); }; class MyOwnStatusListener : public MyStatusListener { public: virtual void myStatusChanged(MyStatusEvent event); }; //Global Variables STSession* pSTSession; STUserStatus myUserStatus; bool bStopMessageLoop; MyOwnStatusListener* pMyStatusListener; //Main int wmain(int argc, wchar_t *argv[ ], wchar_t *envp[ ] ) { printf("starting main....\n"); if (argc < 4) { printf("Exiting with code -1 ,not enough parameters.\n"); return -1; } //Host Name wstring strHostName(argv[1]); printf("Host Name : %S\n",strHostName.c_str()); //User Name wstring strUserName(argv[2]); printf("User Name : %S\n",strUserName.c_str()); //Password wstring strPassword(argv[3]); //Status Type
299
short sStatusType(_wtoi(argv[4])); printf("Status Type : %d\n",sStatusType); //Status Description wstring strSTatusDescription(argv[5]); printf("Status Description: %S\n",strSTatusDescription.c_str()); //Set myUserStatus object printf("Setting myUserStatus object.....\n"); myUserStatus.setStatusType(sStatusType); myUserStatus.setStatusDescription(strSTatusDescription); printf("myUserStatus object was set.\n"); //Start Session printf("Creating new STSession.....\n"); pSTSession = new STSession(L"Win32STatus"); printf("STSession* created : 0x%X\n",(long)pSTSession); printf("Stating the STSession.....\n"); pSTSession->start(); printf("STSession started.\n"); //Creating new CommunityServive printf("Creating new CommunityService.....\n"); CommunityService* pCommunityService = NULL; pCommunityService = new CommunityService(pSTSession); printf("CommunityService* created : 0x%X\n",(long)pCommunityService); //Creating new MyLoginListener printf("Creating new MyLoginListener.....\n"); MyLoginListener* pLoginListener = NULL; pLoginListener = new MyLoginListener; printf("MyLoginListener* created : 0x%X\n",(long)pLoginListener); //Adding MyLoginListener printf("Adding MyLoginListener to the CommunityService.....\n"); pCommunityService->addLoginListener(pLoginListener); printf("MyLoginListener* Added.\n"); //Message Loop printf("Stating Message Loop......\n"); MSG msg; bool bFirstLoop = true; bStopMessageLoop = false; while(GetMessage(&msg,NULL,0,0) && !bStopMessageLoop) { if (bFirstLoop) { //Login To Sametime printf ("Loging to Sametime.....\n");
300
if (pCommunityService) pCommunityService->loginByPassword(strHostName,strUserName,strPassword); bFirstLoop = false; } DispatchMessage(&msg); } printf("Message Loop Stoped.\n"); //Remove MyLoginListener printf ("Removing MyloginListener from CommunityService.....\n"); if (pCommunityService && pLoginListener) pCommunityService->removeLoginListener(pLoginListener); printf ("MyloginListener Removed.\n"); //Delete MyloginListener printf ("Deleting MyloginListener.....\n"); if (pLoginListener) delete pLoginListener; pLoginListener = NULL; printf ("MyloginListener* Deleted : 0x%X.\n",(long)pLoginListener); //Stopping the Session printf("Stopping the STSession.....\n"); if (pSTSession) pSTSession->stop(); printf("STSession Stopped.\n"); //Deleting the Session printf("Deleting the STSession.....\n"); if (pSTSession) delete pSTSession; printf("STSession* Deleted : 0x%X.\n",(long)pSTSession); //Exit printf("Exiting with code 0"); return 0; } void MyLoginListener::loggedIn (LoginEvent event) { printf("loggedIn event has been called\n"); //Getting Community Service object printf("Getting the CommunityService object.....\n"); CommunityService* pCommunityService = NULL; if (pSTSession) pCommunityService = (CommunityService*) (pSTSession->getComponent(COMMUNITY_COMP_NAME)); printf("CommunityService* : 0x%X\n",(long)pCommunityService);
301
//Getting Login object printf("Getting the Login object.....\n"); Login* pLogin = NULL; if (pCommunityService) pLogin = pCommunityService->getLogin(); printf("Login* : 0x%X\n",(long)pLogin); //Creating new MyOwnStatusListener printf("Creating new MyOwnStatusListener.....\n"); pMyStatusListener = NULL; pMyStatusListener = new MyOwnStatusListener; printf("MyOwnStatusListener* created : 0x%X\n", (long)pMyStatusListener); //Adding MyLoginListener printf("Adding MyOwnStatusListener to the Login.....\n"); pLogin->addMyStatusListener(pMyStatusListener); printf("MyOwnStatusListener* Added.\n"); //Change the Status printf("Changing the status.....\n"); if (pLogin) pLogin->changeMyStatus(myUserStatus); } void MyLoginListener::loggedOut(LoginEvent event) { printf("loggedOut event has been called. reason : 0x%X\n", event.getReason()); bStopMessageLoop = true; } void MyOwnStatusListener::myStatusChanged(MyStatusEvent event) { printf("myStatusChanged event has been called.\n\t\tStatus Type : %d\n\t\tStatus Description : %S\n", event.getStatus().getStatusType(), event.getStatus().getStatusDescription().c_str()); //Getting Community Service object printf("Getting the CommunityService object.....\n"); CommunityService* pCommunityService = NULL; if (pSTSession) pCommunityService = (CommunityService*) (pSTSession->getComponent(COMMUNITY_COMP_NAME)); printf("CommunityService* : 0x%X\n",(long)pCommunityService); //Logging out from Sametime
302
303
As we said, this is a very simple program, but enough to illustrate the differences between programming for MFC and Win32.
10.2.1 Libraries
The only difference between MFC applications and Win32 applications is that in MFC applications you link with the starchmfc.lib library and for Win32 application you should link with the starch.lib. Set your project settings to use the starch.lib if you write a Win32 application. All other Sametime libraries can be used for both MFC and win32 applications.
You can put the message-loop anywhere in your application. Refer to Example 10-1 on page 298 to see where we put it in our Win32Status sample.
304
Important: Without Message-Loop in your Win32 application, you will not be able to get any response from the C++ Toolkit Services and objects.
This concludes our discussion of Win32 coding considerations when using the Sametime C++ toolkit.
10.3 Summary
In this chapter we showed you how to write a very simple Win32 program with the Sametime C++ toolkit. If you want to use the Sametime C++ toolkit in a Win32 program, you must link with a library other than the one used with MFC. If your program must support wide characters, you must use the wmain function. And finally, make sure your Win32 program has a message loop. Otherwise it will not be able to receive the events that the full Sametime architecture is based upon.
305
306
Part 4
Part
307
308
11
Chapter 11.
309
11.1 Overview
The Sametime COM toolkit provides the basic Sametime community services, delivered as a standard DLL with interfaces that represent the services provided. This toolkit can be used to enable applications with Sametime in any development environment that supports COM, such as Visual Basic/VBA (used in Microsoft Office and other applications), and JavaScript in Microsoft Internet Explorer. The COM Toolkit provides the basic functionality required to develop a Sametime application. The COM Toolkit supports Automation OLE, and is delivered as a DLL file. A tlb file is provided as well, to support importing of all the interface names. Using the development tools with which you are familiar, you can write your own user interface on top of the Sametime services. Figure 11-1 shows a sample application, Buddy List, written using the COM Toolkit.
310
The Sametime COM toolkit exposes the following subset of the Sametime community services, described in the following sections: Community service Lookup service Awareness service Instant messaging service
Community service
The Community service is the core service you must use to log in and log out from the Sametime server. In addition to login, the Community service provides the ability to change your online status, to receive administrator messages, and to request to change your displayed user name if you logged on anonymously.
Lookup service
The Lookup service provides the ability to resolve user names to unique user objects, for example, when adding a user to the buddy list.
Awareness service
The Awareness service provides the ability to be aware of the online status of Sametime users. Using this service, you can register to receive notifications of changes in the online status of users. This service is sometimes referred to as People Awareness.
311
2. Click the Toolkit link at the bottom of the page. You might need to scroll down the page to see the link. 3. Click COM Toolkit on the Toolkits page. You are now at the Sametime COM toolkit home page, as shown in Figure 11-2 on page 313.
312
313
Figure 11-3 Adding COM Toolkit reference to your project : Click References menu item.
After clicking the Reference menu item, the References dialog is opened. Once the dialog is opened, search for STComTk.dll and select it. Then click OK , as shown in Figure 11-4 on page 315.
314
Figure 11-4 Adding COM toolkit reference to your project: References Dialog
You can also use the late binding method. To use this youll need the STComTkLib library.
315
As mentioned earlier, the first thing we need to do after creating a new project in Visual Basic is reference the Sametime COM toolkit to the project. See 11.2.3, Adding the COM toolkit reference to your project on page 313 for details.
316
Services
The Login sample will need only the login\logout functionality, so the only service we need is the CommunityService. This service will be held as a private member of the module. To retrieve the CommunityService service from the module, we will provide a public function GetCommunityService which will return its private member MyCommunityServiceas seen in Example 11-2.
Example 11-2 SametimeSession_GetCommunityService Public Function
Private MyCommunityService As CommunityService . . . Public Function GetCommunityService() As CommunityService Set GetCommunityService = MyCommunityService End Function
Initializing
The SametimeSession module provides a public method to initialize all its services. Usually we initialize all the services when the application is loaded. In this sample, and in all the other samples, we will do this when the Form_Load event of the main form is fired. We create and initialize all services by using the New keyword, as seen in Example 11-3 in the InitilizeSession public method.
Example 11-3 SametimeSession_InitializeSession Public Method
Public Sub InitializeSession() Set MyCommunityService = New CommunityService End Sub
317
In this sample, the LoginForm shown in Figure 11-6 will be used as the main form of the application and will be the first one to be loaded.
Example 11-4 LoginForm Code
Private WithEvents MyCommunityService As CommunityService Private Sub Form_Load() SametimeSession.InitializeSession Set MyCommunityService = SametimeSession.GetCommunityService End Sub Private Sub Login_Click() MyCommunityService.LoginByPassword Host, UserName, Password End Sub Private Sub MyCommunityService_LoggedIn(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You are logged in to sametime." Login.Caption = "Logout" End Sub Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You have been logged out." & vbNewLine & _ "Reason : 0x" & Hex(ev.Reason) Login.Caption = "Login" End Sub
Private members
We will declare one private member which is the CommunityService, so we can use it to log into Sametime. This member will be declared with events so we can get the LoggedIn/loggedOut events, as shown in Example 11-5.
318
Form_Load event
Once the Application is started, the Form_Load event is fired because this is the application main form.
Example 11-6 LoginForm_FormLoad event
Private Sub Form_Load() SametimeSession.InitializeSession Set MyCommunityService = SametimeSession.GetCommunityService End Sub
As seen in Example 11-6, after calling SametimeSession.InitializeSession we set the private member MyCommunityService to CommunityService, which was already initialized in the SametimeSession, by using SametimeSession.GetCommunityService public method.
Login_Click event
Once the user clicks the Login button, the Login_Click event is fired. Using the HostName, UserName, and Password edit fields, we will try to log into Sametime by calling the MyCommunityService.LoginByPassword function, as shown in Example 11-7.
Example 11-7 LoginForm_Login_Click event
Private Sub Login_Click() MyCommunityService.LoginByPassword Host, UserName, Password End Sub
After calling the MyCommunityService.LoginByPassword function, we wait for one of these events: LoggedIn or LoggedOut.
MyCommunityService_LoggedIn event
Once we have successfully logged into Sametime, the MyCommunityService_LoggedIn event will be fired. In this sample we will create a message box which will identify it, as shown in Example 11-8.
Example 11-8 LoginForm_MyCommunityService_LoggedIn event
Private Sub MyCommunityService_LoggedIn(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You are logged in to sametime." End Sub
319
MyCommunityService_LoggedOut event
Once we have failed to log into Sametime, the MyCommunityService_LoggedOut event is fired. Using the LoginEvent which is provided once the event is fired, we can get the reason for the failed login. Refer to the COM Toolkit Reference Guide for more details about errors using the ISTError enum.
Example 11-9 LoginForm_MyCommunityService_LoggedOut event
Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You have been logged out." & vbNewLine & _ "Reason : 0x" & Hex(ev.Reason) End Sub
As seen in Example 11-9, we create a message box once the MyCommunityService_LoggedOut event is fired and provide it the reason code as we get it from the LoginEvent.
320
321
Set GetCommunityService = MyCommunityService End Function Public Function GetLookupService() As LookupService Set GetLookupService = MyLookupService End Function Public Function GetAwarenessServie() As AwarenessService Set GetAwarenessServie = MyAwarenessService End Function
Services
We add the LookupService and AwarenessService private members to the SametimeSession Module, which already has the CommunityService private member we added in the Login sample. See Example 11-11.
Example 11-11 SametimeSession Private Members
Private MyCommunityService As CommunityService Private MyLookupService As LookupService Private MyAwarenessService As AwarenessService
We also provide public functions to retrieve these services, as you can see in Example 11-12.
Example 11-12 SametimeSession Access to the services
Public Function GetCommunityService() As CommunityService Set GetCommunityService = MyCommunityService End Function Public Function GetLookupService() As LookupService Set GetLookupService = MyLookupService End Function Public Function GetAwarenessServie() As AwarenessService Set GetAwarenessServie = MyAwarenessService End Function
Initializing
We must initialize the new private members that we added, as shown in Example 11-13.
Example 11-13 Awareness SametimeSession_InitializeSession public method
Public Sub InitializeSession() Set MyCommunityService = New CommunityService Set MyLookupService = New LookupService
322
323
Public Sub AddUserToList(ByRef User As STUser) If Not IsNull(MyWatchList) Then MyWatchList.AddUser User AwarenessListView.ListItems.Add 1, User.Id.Id, User.Name End If End Sub Private Sub ChangeStatus_Click() Dim MyChangeStatusForm As New ChangeStatusForm MyChangeStatusForm.Left = Me.Left + Me.Width MyChangeStatusForm.Top = Me.Top MyChangeStatusForm.Show vbModeless, Me End Sub Private Sub Form_Load() SametimeSession.InitializeSession Set MyCommunityService = SametimeSession.GetCommunityService Set MyAwarenessService = SametimeSession.GetAwarenessServie End Sub Private Sub Login_Click() If MyCommunityService.IsLoggedIn Then MyCommunityService.LogOut Else Dim MyLoginForm As New LoginForm MyLoginForm.Left = Me.Left + Me.Width MyLoginForm.Top = Me.Top MyLoginForm.Show vbModeless, Me End If End Sub Private Sub AddUser_Click() If MyCommunityService.IsLoggedIn Then Dim MyAddUserForm As AddUserForm Set MyAddUserForm = New AddUserForm MyAddUserForm.Left = Me.Left + Me.Width MyAddUserForm.Top = Me.Top MyAddUserForm.SetAwarenessForm Me MyAddUserForm.Show vbModeless, Me End If End Sub Private Sub MyCommunityService_AdminMsgReceived(ByVal ev As STComTkLib.IAdminMsgEvent) MsgBox ev.MsgText, vbInformation, "Admin Message" End Sub Private Sub MyCommunityService_LoggedIn(ByVal ev As STComTkLib.ILoginEvent)
324
Login.Caption = "Logout" AddUser.Enabled = True ChangeStatus.Enabled = True End Sub Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) Login.Caption = "Login" AddUser.Enabled = False ChangeStatus.Enabled = False End Sub Private Sub MyAwarenessService_ServiceAvailable(ByVal ev As STComTkLib.IAwarenessServiceEvent) Set MyWatchList = MyAwarenessService.CreateWatchList AwarenessListView.Enabled = True End Sub Private Sub MyAwarenessService_ServiceUnavailable(ByVal ev As STComTkLib.IAwarenessServiceEvent) If Not IsNull(MyWatchList) Then MyWatchList.Reset Set MyWatchList = Nothing End If AwarenessListView.ListItems.Clear AwarenessListView.Enabled = False End Sub Private Sub MyWatchList_UserStatusChanged(ByVal ev As STComTkLib.IStatusEvent) Dim IconNumber As Integer Dim Item As ListItem Set Item = FindUserInList(ev.WatchedUser.Id) If Item.Index > 0 Then Select Case ev.WatchedUser.Status.StatusType Case IST_USER_STATUS_ACTIVE IconNumber = 1 Case IST_USER_STATUS_AWAY IconNumber = 2 Case IST_USER_STATUS_DND IconNumber = 3 Case Else IconNumber = 0 End Select Dim Index As Integer Dim Key, Text As String Index = Item.Index Key = Item.Key Text = Item.Text AwarenessListView.ListItems.Remove Item.Index
325
AwarenessListView.ListItems.Add Index, Key, Text, IconNumber, IconNumber AwarenessListView.Refresh End If End Sub Private Function FindUserInList(ByRef UserId As STId) As ListItem Dim Item As ListItem For Each Item In AwarenessListView.ListItems If Item.Key = UserId.Id Then Set FindUserInList = Item End If Next Item End Function
Private members
The AwarenessForm holds the following private members: CommunityService - So we know when the user is logged in\logged out, and to get administrator messages. AwarenessService - So we know whether the Awareness service is available or not. WatchList - Created by the AwarenessService when we get the ServiceAvailable event; it is used for getting events on users status. As shown in Example 11-15, all members are declared with the WithEvents keyword so we can get all the events from our members.
Example 11-15 AwarenessForm Private Members
Private WithEvents MyCommunityService As CommunityService Private WithEvents MyAwarenessService As AwarenessService Private WithEvents MyWatchList As WatchList
Form_Load event
When the application is started and because AwarenessForm is the application main form, Form_Load event is being fired. This is a good place in your code to initialize the SametimeSession and the private members of the form. See Example 11-16. First we initialize the SametimeSession module, as we did in the Login sample, and then we set all our private members to be referenced to the ones in the SametimeSession.
Example 11-16 AwarenessForm - Form_Load event
Private Sub Form_Load() SametimeSession.InitializeSession
326
Login_Click event
When the user clicks the Login button, the Login_Click event is fired. See Example 11-17. Because the Login button is used also for Logout, when we log into Sametime the first thing we do is to check if we are currently logged in. If we are currently logged in it means that the user wants to log out of Sametime, so we log out. Otherwise we need to create the LoginForm to do the login.
Example 11-17 AwarenessForm - Login_Click event
Private Sub Login_Click() If MyCommunityService.IsLoggedIn Then MyCommunityService.LogOut Else Dim MyLoginForm As New LoginForm MyLoginForm.Left = Me.Left + Me.Width MyLoginForm.Top = Me.Top MyLoginForm.Show vbModeless, Me End If End Sub
You can refer to Log into Sametime - the LoginForm on page 332 for more details about the LoginForm.
MyCommunityService_LoggedIn event
Once we have successfully logged into Sametime, the MyCommunityService_ LoggedIn event is fired. As seen in Example 11-18, the first thing we do is change the Login button caption to Logout, so when the user decides to logout from the server he can press this button. Second, we enable both AddUser and ChangeStatus buttons because now the user can use them.
Note: You can resolve users and change your status only when you are logged into Sametime. If you do this when you are offline, these changes will not take any effect in the Sametime server.
Example 11-18 AwarenessForm - MyCommunityService_LoggedIn event
Private Sub MyCommunityService_LoggedIn(ByVal ev As STComTkLib.ILoginEvent) Login.Caption = "Logout" AddUser.Enabled = True
327
MyCommunityService_LoggedOut event
If we have failed to log into Sametime, the MyCommunityService_LoggedOut event is fired. See Example 11-19. First we change the Login button caption to Login so the user can try to login again by pressing this button. Next, we disable both AddUser and ChangeStatus buttons because now the user is offline and cant use these buttons.
Example 11-19 AwarenessForm - MyCommunityService_LoggedOut event
Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) Login.Caption = "Login" AddUser.Enabled = False ChangeStatus.Enabled = False End Sub
MyCommunityService_AdminMsgReceived event
Accepting administrator messages is possible if we listen to the MyCommunityService_AdminMsgReceived event. Once the MyCommunityService_AdminMsgReceived event is fired, we can access the message which was sent from the administrator by using the AdminMsgEvent.MsgEvent property from the AdminMsgEvent object, as seen in Example 11-20.
Example 11-20 AwarenessForm - MyCommunityService_AdminMsgReceived event
Private Sub MyCommunityService_AdminMsgReceived(ByVal ev As STComTkLib.IAdminMsgEvent) MsgBox ev.MsgText, vbInformation, "Admin Message" End Sub
Attention: All properties in Sametime events are read only. You wont be able to change them.
MyAwarenessService_ServiceAvailable event
This event will be fired once we have successfully logged into Sametime or when we are already logged into Sametime and the AwarenessService on the server goes down and comes up again.
328
See Example 11-21. When the MyAwarenessService_ServiceAvailable event is fired, the first thing we do is create a new WatchList object by using the MyAwarenessService.CreateWatchlist function.
Example 11-21 AwarenessForm - MyAwarenessService_ServiceAvailable event
Private Sub MyAwarenessService_ServiceAvailable(ByVal ev As STComTkLib.IAwarenessServiceEvent) Set MyWatchList = MyAwarenessService.CreateWatchList AwarenessListView.Enabled = True End Sub
For more information about the WatchList Interface refer to the Sametime COM Toolkit User Guide that is delivered as a PDF file together with the toolkit.
MyAwarenessService_ServiceUnavailable event
This event is fired once we have logged out from Sametime from any reason and also when the AwarenessService on the server went down. You can see in Example 11-22 how we handle the MyAwarenessService_ServiceUnavailable event. We reset the MyWatchList object by calling the MyWatchlist.Reset function and then reference it to Nothing. After this we clear the AwarenessListView control and disable it.
Note: Once the Awareness Service is unavailable, all WatchList objects are disabled, so you wont get any event while the service is down.
Example 11-22 AwarenessForm - MyAwarenessService_ServiceUnavailable event
Private Sub MyAwarenessService_ServiceUnavailable(ByVal ev As STComTkLib.IAwarenessServiceEvent) If Not IsNull(MyWatchList) Then MyWatchList.Reset Set MyWatchList = Nothing End If AwarenessListView.ListItems.Clear AwarenessListView.Enabled = False
AddUser_Click event
When the user decides that he wants to add a new user to his AwarenessList, he presses the AddUser button. Once the AddUser button is pressed, the AddUser_Click event is fired.
Example 11-23 AwarenessForm - AddUser_Click event
Private Sub AddUser_Click()
329
If MyCommunityService.IsLoggedIn Then Dim MyAddUserForm As AddUserForm Set MyAddUserForm = New AddUserForm MyAddUserForm.Left = Me.Left + Me.Width MyAddUserForm.Top = Me.Top MyAddUserForm.SetAwarenessForm Me MyAddUserForm.Show vbModeless, Me End If End Sub
See Example 11-23. We check whether we are currently logged into the server, and if so, we create a new AddUserForm, which will do all the resolving work with the LookupService. For more information about the AddUserForm, refer to Adding users to the AwarenessList - the AddUserForm on page 335.
The second thing we do is add the user to the AwarenessListView control by calling the AwarenessListView.ListItems.Add function and providing it with the UserId property as a unique key and the User.Name as the string which represents the user name.
Example 11-24 AwarenessForm - AddUsertoList Public Method
Public Sub AddUserToList(ByRef User As STUser) If Not IsNull(MyWatchList) Then MyWatchList.AddUser User AwarenessListView.ListItems.Add 1, User.Id.Id, User.Name End If End Sub
330
MyWatchList_UserStatusChanged event
The UserStatusChanged event is fired once a user has changed his own status or once a new user is added to the WatchList object.
Example 11-25 Awareness Form - MyWatchList_UserStatusChanged event
Private Sub MyWatchList_UserStatusChanged(ByVal ev As STComTkLib.IStatusEvent) Dim IconNumber As Integer Dim Item As ListItem Set Item = FindUserInList(ev.WatchedUser.Id) If Item.Index > 0 Then Select Case ev.WatchedUser.Status.StatusType Case IST_USER_STATUS_ACTIVE IconNumber = 1 Case IST_USER_STATUS_AWAY IconNumber = 2 Case IST_USER_STATUS_DND IconNumber = 3 Case Else IconNumber = 0 End Select Dim Index As Integer Dim Key, Text As String Index = Item.Index Key = Item.Key Text = Item.Text AwarenessListView.ListItems.Remove Item.Index AwarenessListView.ListItems.Add Index, Key, Text, IconNumber, IconNumber AwarenessListView.Refresh End If End Sub
As you see in Example 11-25, once MyWatchList_UserStatusChanged event is fired the following steps are taken: 1. Try to find if the user exists in the AwarenessListView. 2. Check the user new status by using the ev.WatchedUser.Status.StatusType property, and attach to it new icon which identifies its new status. 3. Replace the old ListItem with the updated one so the status changes will be reflected on the AwarenessListView control. Refer to the Sametime COM Toolkit User Guide for more information about StatusEvent and STWatchedUser interfaces. This guide is delivered together with the toolkit as a PDF file.
331
ChangeStatus_Click event
A user can change online status by clicking on the ChangeStatus button. Once the user clicks on the ChangeStatus button, the ChangeStatus_Click event is fired, as shown in Example 11-27. We create a new ChangeStatusForm and place it on the right corner of the AwarenessForm.
Example 11-27 AwarenessForm - ChangeStatus_Click event
Private Sub ChangeStatus_Click() Dim MyChangeStatusForm As New ChangeStatusForm MyChangeStatusForm.Left = Me.Left + Me.Width MyChangeStatusForm.Top = Me.Top MyChangeStatusForm.Show vbModeless, Me End Sub
For more information about the ChangeStatusForm, refer to Changing my own status - the ChangeStatusForm on page 338.
332
Private members
The LoginForm holds only one private member, the CommunitySevice. We declare the MyCommunityService as a CommunityService and use the WithEvents keyword to be able to get the LoggedIn\Loggedout events as seen in Example 11-29.
Example 11-29 LoginForm Private Members
Private WithEvents MyCommunityService As CommunityService
Form_Load event
When the user clicks the Login button on the AwarenessForm, the new LoginForm is created. Once a new LoginForm is created, the Form_Load Event is fired.
Example 11-30 LoginForm - Form_Load event
Private Sub Form_Load() Set MyCommunityService = SametimeSession.GetCommunityService End Sub
As seen in Example 11-30, once the Form_Load event is fired we reference the MyCommunityService private member to the CommunityService member in the SametimeSession module.
333
Login_Click event
When the user has entered all the necessary parameters in the LoginForm and clicked the Login button, the Login_Click event is fired. As seen in Example 11-31, once the Login_Click event is fired we call the MyCommunityService.LoginByPassword function with the HostName, UserName, and password text fields text.
Example 11-31 LoginForm - Login_Click
Private Sub Login_Click() MyCommunityService.LoginByPassword Host, UserName, Password End Sub
MyCommunityService_LoggedIn event
Once we have successfully logged into Sametime, this form has finished its work and can be closed (AwarenessForm will also get the event from the CommunityService and will handle this event), as seen in Example 11-32.
Example 11-32 LoginForm - MyCommunityService_LoggedIn event
Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You have been logged out." & vbNewLine & _ "Reason : 0x" & Hex(ev.Reason) End Sub
Tip: You can reference as many Sametime objects as you like to get Sametime events. All events in the referenced objects will be fired. This is useful when you want to get the same event in different forms, modules, or classes.
MyCommunityService_LoggedOut event
If the login to Sametime fails, the MyCommunityService_LoggedOut event is fired. Using the LoginEvent which is provided once the event is fired, we can get the reason for the failed login. Refer to the COM Toolkit Reference Guide for more details about errors using the ISTError enum.
Example 11-33 LoginForm - MyCommunityService_LoggedOut event
Private Sub MyCommunityService_LoggedOut(ByVal ev As STComTkLib.ILoginEvent) MsgBox "You have been logged out." & vbNewLine & _ "Reason : 0x" & Hex(ev.Reason) End Sub
334
As seen in Example 11-9, we create a message box if the MyCommunityService_LoggedOut event is fired and provide it with the reason code that we get from the LoginEvent. At this point the form will not be closed. The message box appears and notifies the user of the reason for the login failure. It also gives another chance to correct the login parameters.
If the resolving process succeeds, it calls the AwarenessForm.AddUserToList public method to add the user to the AwarenessForm AwarenessList control. The AddUserForm uses the LookupService to create a Resolver object, which is used to resolve the name the user provides.
Example 11-34 AddUserForm Code
Private MyLookupService As LookupService Private WithEvents MyResolver As Resolver Private MyAwarenessForm As AwarenessForm Public Sub SetAwarenessForm(ByRef AwarenessFrm As AwarenessForm) Set MyAwarenessForm = AwarenessFrm End Sub Private Sub Add_Click() MyResolver.Resolve UserName End Sub Private Sub Form_Load() Set MyLookupService = SametimeSession.GetLookupService Set MyResolver = MyLookupService.CreateResolver(True, True) End Sub Private Sub MyResolver_Resolved(ByVal ev As STComTkLib.IResolveEvent)
335
MyAwarenessForm.AddUserToList ev.GetResolvedUser UserName.SetFocus End Sub Private Sub MyResolver_ResolveFailed(ByVal ev As STComTkLib.IResolveEvent) MsgBox "The name " & ev.FailedName & " was not found try again" UserName.SetFocus End Sub
Private members
The AddUserForm holds three private members: MyLookupService - LookupService member used to create a resolver object. MyResolver - Resolver member used to resolve users. MyAwarenessForm - AwarenessForm member used to call back to the AwarenessForm.AddUserToList method. As seen in Example 11-35, the MyResolver private member is declared using the WithEvents keyword so we can get the Resolved\RsolvedFailed events.
Example 11-35 AddUserForm Private Members
Private MyLookupService As LookupService Private WithEvents MyResolver As Resolver Private MyAwarenessForm As AwarenessForm
Form_Load event
When the user clicks the AddUser button on the AwarenessForm, a new AddUserForm is created. Once the new AddUserForm is created, the Form_Load event is fired.
336
See Example 11-37. Once the Form_Load event is fired, we set the MyLookupService private member to the one in the SametimeSession and we set MyResolver private member to a new Resolver by using MyLookupService.CreateResolver function. The two parameters we provide the MyLookupService.CreateResolver function are: True to OnlyUnique and True to ExhaustiveLookup.
Example 11-37 AddUserForm - Form_Load
Private Sub Form_Load() Set MyLookupService = SametimeSession.GetLookupService Set MyResolver = MyLookupService.CreateResolver(True, True) End Sub
Add_Click event
Once the user clicks the Add button the Add_Click event is fired. As seen in Example 11-38, once the Add_Click event is fired we call MyResolver.Resolve function to resolve the name the user provided in the UserName text field.
Example 11-38 AddUserForm - Add_Click event
Private Sub Add_Click() MyResolver.Resolve UserName End Sub
MyResolver_Resolved event
The Resolved event is fired when the name the user provided has been successfully resolved. As seen in Example 11-39, once the MyResolver_Resolved event is fired we call the AwarenessForm.AddUserToList public methods by using the MyAwarenessForm private members. When we call the MyAwarenessForm.AddUserToList method, we pass this method the resolved user we get from the ResolveEvent. This is done by calling ResolveEvent.GetResolvedUser function.
Example 11-39 AddUserForm - MyResolver_Resolved event
Private Sub MyResolver_Resolved(ByVal ev As STComTkLib.IResolveEvent) MyAwarenessForm.AddUserToList ev.GetResolvedUser UserName.SetFocus End Sub
337
MyResolver_ResolveFailed event
The ResolveFailed event is fired if the server fails to resolve the name the user has provided. See Example 11-40. If the MyResolver_ResolveFailed event is fired, we create a message box showing the name that failed to be resolved. This name is a property of the ResolveEvent.
Example 11-40 AddUserForm - MyResolver_ResolveFailed event
Private Sub MyResolver_ResolveFailed(ByVal ev As STComTkLib.IResolveEvent) MsgBox "The name " & ev.FailedName & " was not found try again" UserName.SetFocus End Sub
This form has one ImageCombo control (StatusImageCombo), which represents the current and the new statuses, a status description field so we can add some more text to describe the new status, and a Change button to change the status. When the form is loaded, StatusImageCombo is used for showing the current status of the user. The ChangeStatusForm also has an ImageList control (StatusImageList), which provides the StatusImageCombo with its icons. You can see the initialization code for the form in Example 11-41 on page 338.
Example 11-41 ChangeStatusForm Code
Private WithEvents MyCommunityService As CommunityService Private WithEvents MyLogin As Login Private Sub Form_Load()
338
Set MyCommunityService = SametimeSession.GetCommunityService If Not IsNull(MyCommunityService) Then Set MyLogin = MyCommunityService.Login End If StatusImageCombo.ComboItems.Add 1, "ActiveStatus", "Active", 1 StatusImageCombo.ComboItems.Add 2, "AwayStatus", "Away", 2 StatusImageCombo.ComboItems.Add 3, "DNDStatus", "Do Not Disturb", 3 Select Case MyLogin.GetMyStatus.StatusType Case IST_USER_STATUS_ACTIVE StatusImageCombo.ComboItems.Item(1).Selected = True Case IST_USER_STATUS_AWAY StatusImageCombo.ComboItems.Item(2).Selected = True Case IST_USER_STATUS_DND StatusImageCombo.ComboItems.Item(3).Selected = True End Select End Sub Private Sub ChangeStatus_Click() If StatusDescription = "" Then MsgBox "Please enter ststus description" StatusDescription.SetFocus Else Dim UserStatus As New STUserStatus Select Case StatusImageCombo.SelectedItem.Key Case "ActiveStatus" UserStatus.StatusType = IST_USER_STATUS_ACTIVE Case "AwayStatus" UserStatus.StatusType = IST_USER_STATUS_AWAY Case "DNDStatus" UserStatus.StatusType = IST_USER_STATUS_DND End Select UserStatus.StatusDescription = StatusDescription MyLogin.ChangeMyStatus UserStatus End If End Sub Private Sub MyLogin_MyStatusChanged(ByVal ev As STComTkLib.IMyStatusEvent) Unload Me End Sub
Private members
The ChangeStatusForm holds two private members: MyCommunityService and MyLogin. It holds the MyCommunityService parameter so it can get the Login object, and a Login parameter so we can change our own status. As seen in Example 11-42, the MyLogin private member is declared with the WithEvents keyword so we can get the MyStatusChanged event.
339
Form_Load event
When the user clicks the ChangeStatus button on the AwarenessForm, a new ChangeStatusForm is created. Once a new ChangeStatusForm is created, the Form_Load event is fired. See Example 11-43. When the Form_Load event is fired, the following steps are taken: 1. The MyCommunityService object is referenced to the one in the SametimeSession. 2. The MyLogin object is referenced to the Login parameter of MyCommunityService object. 3. All items are added to the StatusImageCombo control. 4. The selected item in the StatusImageCombo control is set by getting my status from the MyLogin object.
Example 11-43 ChangeStatusForm - Form_Load event
Private Sub Form_Load() Set MyCommunityService = SametimeSession.GetCommunityService If Not IsNull(MyCommunityService) Then Set MyLogin = MyCommunityService.Login End If StatusImageCombo.ComboItems.Add 1, "ActiveStatus", "Active", 1 StatusImageCombo.ComboItems.Add 2, "AwayStatus", "Away", 2 StatusImageCombo.ComboItems.Add 3, "DNDStatus", "Do Not Disturb", 3 Select Case MyLogin.GetMyStatus.StatusType Case IST_USER_STATUS_ACTIVE StatusImageCombo.ComboItems.Item(1).Selected = True Case IST_USER_STATUS_AWAY StatusImageCombo.ComboItems.Item(2).Selected = True Case IST_USER_STATUS_DND StatusImageCombo.ComboItems.Item(3).Selected = True End Select End Sub
340
Important: The Login property of the CommunityService is valid only when the user is currently logged into the server.
ChangeStatus_Click event
The ChangeStatus_Click event is fired when the user clicks the ChangeStatus button. As seen in Example 11-44, once the ChangeStatus_Click event is fired, we check to see whether the user has entered a status description. If he has not, we create a message box telling him to put one in; if he has, we create a new STUserStatus object by using the New keyword. We fill the UserStatus object with the new status according to the StatusImageCombo selected item and the new description the user entered. Finally, we change our status by calling the Mylogin.ChangeMyStatus function and passing it the UserStatus object.
Example 11-44 ChangeStatusForm - ChangeStatus_Click event
Private Sub ChangeStatus_Click() If StatusDescription = "" Then MsgBox "Please enter ststus description" StatusDescription.SetFocus Else Dim UserStatus As New STUserStatus Select Case StatusImageCombo.SelectedItem.Key Case "ActiveStatus" UserStatus.StatusType = IST_USER_STATUS_ACTIVE Case "AwayStatus" UserStatus.StatusType = IST_USER_STATUS_AWAY Case "DNDStatus" UserStatus.StatusType = IST_USER_STATUS_DND End Select UserStatus.StatusDescription = StatusDescription MyLogin.ChangeMyStatus UserStatus End If End Sub
MyLogin_MyStatusChanged event
The MyStatusChanged event is fired when the user successfully changed his own status.
341
Attention: When two Sametime clients are running under the same user on the same machine and one of the clients changes his own status, MyStatusChanged will be fired in both clients
As seen in Example 11-45, once the MyLogin_MyStatusChanged event is fired, the only thing we do is to close the form.
Note: If the user added himself to the AwarenessList, the UserStatusChanged event will also be fired. That way, the AwarenessList will be updated with the new status.
Example 11-45 ChangeStatusForm - Mylogin_MyStatusChanged event
Private Sub MyLogin_MyStatusChanged(ByVal ev As STComTkLib.IMyStatusEvent) Unload Me End Sub
342
Public Sub InitializeSession() Set MyCommunityService = New CommunityService Set MyLookupService = New LookupService Set MyAwarenessService = New AwarenessService Set MyImService = New InstantMessagingService End Sub Public Function GetCommunityService() As CommunityService Set GetCommunityService = MyCommunityService
343
End Function Public Function GetLookupService() As LookupService Set GetLookupService = MyLookupService End Function Public Function GetAwarenessServie() As AwarenessService Set GetAwarenessServie = MyAwarenessService End Function Public Function GetImServie() As InstantMessagingService Set GetImServie = MyImService End Function
Services
The only service which remains to be added in this sample is the Instant Messaging Service. This service is also being held as a private member in the SametimeSession, and provides a get function as seen in Example 11-47.
Example 11-47 SametimeSession - Get the Im Service
Private MyImService As InstantMessagingService . . . Public Function GetImServie() As InstantMessagingService Set GetImServie = MyImService End Function
You may think that there is a spelling error in the GetImServie function, but we keep it as it is to make sure it matches the downloadable code.
Initializing
As we did in the Awareness sample, we initialize the new service in the InitializeSession public method, as seen in Example 11-48.
Example 11-48 SametimeSession - InitializeSession Public Method
Public Sub InitializeSession() Set MyCommunityService = New CommunityService Set MyLookupService = New LookupService Set MyAwarenessService = New AwarenessService Set MyImService = New InstantMessagingService End Sub
344
We are basically using the same code in the BuddyListForm as in the AwarenessForm except for the name of the form. Thus we only show the code added to the form in Example 11-49. You can see the code for the AwarenessForm in Example 11-14 on page 323.
Example 11-49 BuddyListForm Code
.... Private WithEvents MyImService As InstantMessagingService .... Private Sub AwarenessListView_DblClick() Call Chat_Click End Sub ....
345
Private Sub Chat_Click() Dim Item As ListItem Set Item = AwarenessListView.SelectedItem If Not Item.Key = "" Then Dim User As New STUser User.Name = Item.Text User.Id.Id = Item.Key Dim MyIm As Im Set MyIm = MyImService.CreateIm(User, _ IST_ENC_LEVEL_NON, 1) Dim MyChatForm As New ChatForm MyChatForm.Left = Me.Left + Me.Width MyChatForm.Top = Me.Top MyChatForm.Show vbModeless, Me MyChatForm.SetIm MyIm, False End If End Sub .... Private Function MyImService_ImReceived(ByVal ev As STComTkLib.IImEvent) As Long Dim MyChatForm As New ChatForm MyChatForm.Left = Me.Left + Me.Width MyChatForm.Top = Me.Top MyChatForm.SetIm ev.ReceivedIm, True End Function .... End Function
Private members
We have added one more private member, the InstantMessagingService. As seen in Example 11-50, we declare MyImService a private member using the keyword WithEvents so we can get the ImReceived event.
Example 11-50 BuddyList Private Members
Private Private Private Private WithEvents WithEvents WithEvents WithEvents MyCommunityService As CommunityService MyAwarenessService As AwarenessService MyImService As InstantMessagingService MyWatchList As WatchList
346
Form_Load event
In the Form_Load event we added the initialization of MyImService private member, as seen in Example 11-51.
Example 11-51 BuddyListForm - Form_Load event
Private Sub Form_Load() SametimeSession.InitializeSession Set MyCommunityService = SametimeSession.GetCommunityService Set MyAwarenessService = SametimeSession.GetAwarenessServie Set MyImService = SametimeSession.GetImServie End Sub
Chat_Click event
When the user clicks the Chat button the Chat_Click event is fired. As seen in Example 11-52, once the Chat_Click event is fired, we try to get the selected user from the AwarenessListView and we check its key. If we succeed in getting the key, we create a new STUser object by using the New keyword. After creating a new User, we set its ID and Name to the same ones as the ListItem which was selected. Now we create a new Im object by using the MyImService.CreateIm function, passing it the User object, the encryption level, and the type of the Im.
Note: All Ims in Sametime Clients like Connect and Java Connect are created with type 1. You can create Ims with any type you like, but they wont be accepted by these clients. For example, you can write your own client using type 1002, which will be recognized only by your client.
After we have a valid Im object MyIm, we create a new ChatForm, placing it on the right corner of the BuddylistForm, and finally, setting it the MyIm object by calling MyChatForm.SetIm, and passing it MyIm object and False (to show that this is not a received Im). For more information about ChatForm, refer to Chatting with others - the ChatForm on page 348.
Example 11-52 BuddyListForm - Chat_Click event
Private Sub Chat_Click() Dim Item As ListItem Set Item = AwarenessListView.SelectedItem If Not Item.Key = "" Then Dim User As New STUser User.Name = Item.Text User.Id.Id = Item.Key Dim MyIm As Im Set MyIm = MyImService.CreateIm(User, _ IST_ENC_LEVEL_NON, 1)
347
Dim MyChatForm As New ChatForm MyChatForm.Left = Me.Left + Me.Width MyChatForm.Top = Me.Top MyChatForm.Show vbModeless, Me MyChatForm.SetIm MyIm, False End If End Sub
MyImService_ImReceived event
The ImReceived event is fired when another user sent you a new Im successfully. See Example 11-53. Once the MyImService_ImReceived event is fired, we create a new ChatForm, placing it in the right corner of the BuddyListForm, and finally, pass it the ReceivedIm property of the ImEvent and True (because this Im has been received).
Example 11-53 BuddylistForm - MyImService_ImReceived event
Private Function MyImService_ImReceived(ByVal ev As STComTkLib.IImEvent) As Long Dim MyChatForm As New ChatForm MyChatForm.Left = Me.Left + Me.Width MyChatForm.Top = Me.Top MyChatForm.SetIm ev.ReceivedIm, True End Function
349
End If TrnscriptText.Text = TrnscriptText.Text & _ MyIm.GetPartner.Name & _ " : " & ev.Text & vbNewLine End Sub Private Sub Send_Click() TrnscriptText.Text = TrnscriptText.Text & _ MyName & " : " & _ MessageText.Text & vbNewLine MyIm.SendText 0, MessageText.Text MessageText.Text = "" End Sub
Private members
The ChatForm has three private members : MyIm - an Im private member, the Im which is used to send/receive text. ImWasReceived - a Boolean private member, this indicates when the Im was created by another user and received by us. MyName - this String private member holds my user name. As seen in Example 11-55, the MyIm private member is declared with the WithEvents keyword so we can get the ImOpened, ImClosed, TextReceived, and some other events related to the Im Interface.
Example 11-55 ChatForm Private Members
Private WithEvents MyIm As Im Private ImWasReceived As Boolean Private MyName As String
350
End Sub
Form_Load event
Once the user clicks on the Chat button or a new Im is received, we create a new ChatForm. If the ChatForm was created because the user pressed the Chat button, we call the Me.Show function in the ChatForm once the ImOpened event is fired, then the Form_Load event is fired. Another case is when an Im is Received. In this case we create a new ChatForm and wait for a text to be received. Once a text is received, we call the Me.Show function inside the ChatForm. Once this is done, the Form_Load event is fired. Refer to Example 11-62 on page 353 to see what we do when the TextReceived event is fired. As seen in Example 11-57, once the Form_Load event is fired, the only thing we do is to set the MyName parameter to the user name we get from the Login object.
Example 11-57 ChatForm - Form_Load event
Private Sub Form_Load() MyName = SametimeSession.GetCommunityService.Login.GetMyUserName End Sub
MyIm_ImOpened event
The ImOpened event is fired once the Im.Open function succeeds in opening the Im.
Note: Youll receive an ImOpened event only if you created a new Im object using the InstantMessagingService.CreateIm function and called Im.Open. You wont get this event when you receive an Im from another user.
See Example 11-58. When MyIm_ImOpened is fired, we first enable all the controls of the form, like TranscriptText and Send button, and then we call Me.Show function to show the window.
Example 11-58 ChatForm - MyIm_ImOpened event
Private Sub MyIm_ImOpened(ByVal ev As STComTkLib.IImEvent) TrnscriptText.Enabled = True MessageText.Enabled = True Send.Enabled = True Me.Show End Sub
351
MyIm_OpenImFailed event
The OpenImFailed event is fired if the Im.Open function failed to open the Im. As seen in , if the MyIm_ImOpenedFailed event is fired, the only thing we do is create a message box containing the reason for the failure.
Example 11-59 ChatForm - MyIm_OpenImFailed event
Private Sub MyIm_OpenImFailed(ByVal ev As STComTkLib.IImEvent) MsgBox "Im open fail." & vbNewLine & _ "Reason : " & ev.Reason End Sub
MyIm_ImClosed event
The ImClosed event is fired when the Im object is closed. Im is closed once one of the users who are chatting calls the Im.Close function and passes it the reason for the close. Another example of a reason for a closed Im is a network problem. This can be verified by the reason we get from ImEvent.Reason property. As seen in Example 11-60, once MyIm_ImClosed is fired, we disable all the ChatForm controls.
Example 11-60 ChatForm - MyIm_ImClosed event
Private Sub MyIm_ImClosed(ByVal ev As STComTkLib.IImEvent) TrnscriptText.Enabled = False MessageText.Enabled = False Send.Enabled = False End Sub
Send_Click event
The Send_Click event is fired when the user clicks the Send button to send the text to another chat user. See Example 11-61. When the Send_Click event is fired, we add the text to the transcript window and send it to the other user using the MyIm.Send function and passing it the text we want to send. Finally, we clear the MessageText field.
Example 11-61 ChatForm - Send_Click
Private Sub Send_Click() TrnscriptText.Text = TrnscriptText.Text & _ MyName & " : " & _ MessageText.Text & vbNewLine MyIm.SendText 0, MessageText.Text MessageText.Text = "" End Sub
352
MyIm_TextReceived event
The TextReceived event is fired when the other chat user sends us new text. As seen in Example 11-62, once MyIm_TextReceived event is fired, we check whether the Im object we have was received; if it is, we check to see if the form is visible. Once both conditions are True, we call Me.Show function to load the form. Details about loading the ChatForm were presented previously. Next we take the text and user name from the ImEvent and put them in the transcript window so the user can see the message that was sent.
Example 11-62 ChatForm - MyIm_TextReceived event
Private Sub MyIm_TextReceived(ByVal ev As STComTkLib.IImEvent) If ImWasReceived And Me.Visible = False And MyIm.IsOpen Then Me.Show vbModeless End If TrnscriptText.Text = TrnscriptText.Text & _ MyIm.GetPartner.Name & _ " : " & ev.Text & vbNewLine End Sub
Close_Click event
The Close_Click event is fired when the user clicks the Close button. You can see in Example 11-63 how we handle the Close_Click event. We close the form by calling the Unload function.
Example 11-63 ChatForm - Close_Click event
Private Sub Close_Click() Unload Me End Sub
Form_Unload event
When we call the Unload function the Form_Unload event is fired. As seen in Example 11-64, after we check whether the MyIm object is valid, we close the Im by calling the MyIm.Close function, passing it IST_OK as a reason for the closing. This will close the Im object in both sides (both users will get the notification for the closing).
Example 11-64 ChatForm - Form_Unload event
Private Sub Form_Unload(Cancel As Integer) If Not IsNull(MyIm) Then MyIm.Close IST_OK End If End Sub
353
This completes our description of the third and final step in our sample. In these three steps we have shown you how to use the Sametime services provided by the Sametime COM toolkit. With this knowledge you can now add awareness and chat capabilities to any applications that supports Visual Basic/VBA or LotusScript.
11.7 Summary
In this chapter we introduced the Sametime COM toolkit. We told you how to get the toolkit and how to use it with Visual Basic. The major part of the chapter covered a sample that shows how to use all the Sametime services offered by the COM toolkit. First, we showed you how to use the community service to log in and out from Sametime. The we added the LookupService to help us find other Sametime users and the AwarenessService to see the status of those other users. Finally, we added the Instant messaging service that allows us to chat with another Sametime user. We have shown how the Sametime COM toolkit makes it easy for you to enable your Visual Basic/VBA/LotusScript applications for real time awareness and communication with a limited effort.
354
12
Chapter 12.
355
356
Description Provides access to additional Sametime functionality and allows you to customize the behavior and look and feel of the live name. (Details follow.) The integration with the site/application should be transparent to the end-user. No installation or code that requires execution privileges (signed applet). The HTML files of the Sametime Links dialogs can be copied to any Web application server. Uses HTTP to communicate with the Sametime server for connectivity (works through firewalls). Supports password, anonymous, and transparent authentication by access token. No need to challenge users that have already logged onto the Web application. The same level of chat encryption provided by other Sametime clients. Windows 95/98/NT/2000, with MSIE 5 and above, and Netscape 4.7x and above. Sametime Links works out-of-the-box on Sametime server version 2.6 and above. A standalone add-on installation will be provided for Sametime 2.5 as well. The UI is HTML, and is fully customizable. It is also ADA-compliant, to the extent that HTML is. Localized for the full set of languages offered by Lotus Sametime. The Sametime server supports very large numbers of Sametime Links users.
No Installation
357
12.1.1 Awareness
Names become live, indicating the online status of the user and allowing real-time communication. The status can be displayed with an optional status indicator next to the name. The default status icons provided can be modified or replaced by the Web author. The status message set by the user is displayed in a floating tool tip over the user name as you can see in Figure 12-2.
358
In addition, the API supports starting an n-way chat by selecting multiple users from the Web page.
12.1.3 Meetings
Additional meeting tools can be added to both the Instant Message and N-Way Chat windows to launch the standard Sametime Meeting Room client.
359
360
361
362
For example, when a Sametime link is clicked, you could display a popup menu providing options for creating an instant message or a meeting, as well as other options like sending mail. Another example is generating an alert each time someone enters a place. Finally, you can customize the look and feel of Sametime links by catching the status changed events and displaying the status differently. An example of this is displaying only people that are currently online in a list.
2. Click the Toolkit link at the bottom of the page. You might need to scroll down the page to see the link. 3. Click Sametime Links Toolkit on the Toolkits page.
363
where codeBase is the URL of the directory where the Sametime Links runtime package is installed. (http://<sametimeserver>/sametime/stlinks, where <sametimeserver> is your Sametime server host name. 2. Add the code that puts the hidden Sametime Links Java applet on the page. This code can be put anywhere within the HTML code of your page, because the applet itself is placed on a hidden zero-size HTML layer.
<script> writeSTLinksApplet (loginName, </script> key, isByToken, port);
where: loginName is the login name of the user. key is the password or token. isByToken is true if you use a token to authenticate, false for password; default is false. port is the port that will be used for communication with the server. The default is port 80.
364
where:
userName is the unique user name. To ensure uniqueness, use the canonical name, or the distinguished name if you are using an LDAP directory. However, note that Sametime server 2.5 cannot resolve distinguished names. Therefore, if you are using Sametime server 2.5 with LDAP, use the users common name. displayName is the display name of the user. bResolve is true if the Sametime server has to resolve the user name. False indicates that the user name is already resolved. options is a string of semicolon-delimited display options. Each element in the list has the format "option:value". The options allow changing the rendering and the behavior of the Sametime link. For example, by specifying "text:yes; icon:no" you create a Sametime link without a status icon.
365
Figure 12-8 Web page enabled for awareness and chat by the Sametime Links toolkit
We enabled the page as described earlier in this section and added the four links you can see on the page (Sren P, Dick, Harriett and Crocodile Frank). In Example 12-1 on page 367 you can see the code we added to the Web page in order to enable it for awareness and chat.
366
Example 12-1 Code required to Sametime enable page shown in Figure 12-8
<html> <head> ... ... <LINK REL=STYLESHEET HREF="http://bigdaddy.lotus.com/sametime/stlinks/runtime/stlinks.css" TYPE="text/css"> <SCRIPT src="http://bigdaddy.lotus.com/sametime/stlinks/runtime/stlinks.js"></SCRIPT> <script> setSTLinksURL("http://bigdaddy.lotus.com/sametime/stlinks/runtime"); </script> </head> <body> <table> ... ... <tr>The members from the trip listed below are regularly online. When their name is green you can click on it to chat with them. </tr> <tr> <script>writeSametimeLink("Soren P", "Sren P", true)</script> <script>writeSametimeLink("Dick", "Dick", true)</script> <script>writeSametimeLink("Harriett", "Harriett", true)</script> <script>writeSametimeLink("Frank Furrer", "Crocodile Frank", true)</script> </tr> ... </table> ... ... <script> writeSTLinksApplet("WebVisitor", "", false); </script> </body> </html>
Note that the password argument for the writeSTLinksApplet statement is an empty string. This means everybody who loads this page gets anonymously logged into the Sametime server. Even though each user will have WebVisitor as their display name, they will get separate sessions. If we already had the users name in a cookie or by other means, we could of course pass that name
367
along when logging in anonymously instead of the generic name WebVisitor. In order to use anonymous users in the context we see here, the Sametime server must be configured to allow anonymous users to resolve users and groups. In Sametime 2.5 this is not allowed by default. Also notice the statements that specify the codebase for the Sametime Links runtime components. You will see that we specify a fully qualified server name. This allows us to host our Web page on the Web server, where it always has been located, and simply uses the Sametime server for Sametime Links functionality. As you saw in this simple sample, the Sametime Links toolkit enables you to add awareness, chat, and chat meeting capability with just a few Web page modifications, and no changes required for your existing infrastructure. This concludes our overview of the Sametime Links toolkit. Check the Sametime Toolkit Developers Guide, installed on your server with the toolkit, for more details on utilizing this capability on your Web applications.
12.3 Summary
In this chapter we covered the capabilities of the Sametime Links toolkit. This toolkit is perfect for Web developers. It allows the addition of awareness and real-time communication capabilities to a Web page without requiring any knowledge about Sametime programming. In addition, the toolkit is lightweight. It only requires download of a small (approximately 20K) applet and some JavaScript and HTML code.
368
Part 5
Part
Appendixes
369
370
Appendix A.
371
372
3. Create a connection table, including a direct connection in the beginning of the list, and test again in this environment. 4. Set up the firewalled and/or proxy-environment like this: a. Read the Sametime Installation Guide (stinstall.pdf) and the Sametime Administrators Guide (sthelpad.pdf) supplied in the directory Docs on the Sametime CD. b. Read the redbook Lotus Sametime 2.0 Deployment Guide, SG24-6206 for an overview of typical scenarios. c. Use a port filtering router between the client and the server, and filter the ports you do not want to use, for instance, the direct connection ports. d. Change the server configuration to listen on another port for direct connections, so that HTTP tunneling is enforced. e. Really use a proxy server and try some typical LAN configurations and proxy setups. 5. Verify a correct server setup with scheduled and instant meetings, as well as the Sametime 3.0 connect clients. 6. Test the application again using the new setup.
Notes:
Direct connections are usually more effective than tunneling connections. The tunneling option is always the last chance to connect, but you may want to discuss connection options with your customers, for instance in a B2B environment. What kind of proxy servers are involved? There are several types of servers called Proxy Servers: HTTP or SOCKS Proxy Servers, which you configure in your browser. Protocol proxy servers/routers, which protect a server from direct access and verify that the requests are protocol consistent.
373
374
Appendix B.
375
A directory called auction will be created, containing: 6666auction.jar HTML files: Auctioneer.html AuctionCustomer.html Image files for the auction items If you stick to this directory structure the HTML files will refer with correct path to the Sametime 3.0 Toolkit jar/cab-files. Otherwise, find the Sametime Java Toolkit files CommRes.jar/cab and STComm.jar/Cab; copy them into this directory and modify the HTML files to point to the files in the same directory. Now you can launch the auctioneer using:
http://<server>/auction/auctioneer.html Important: You must launch the Auctioneer before you start any AuctionCustomer (bidder) clients. This is because the Auctioneer first must create the place to enter. You will receive an error if an AuctionCustomer tries to enter a non-existent place.
376
Similar to JavaDoc, we provide also the complete source in HTML format, generated using a tool named Java2HTML. The usage is similar to JavaDoc, but youll have the Java source with Syntax highlighting. See Figure 12-10 on page 378 for an example of this.
377
In addition, an integrated development environment like VisualAge for Java helps to browse through classes and methods, and easily to make modifications to our samples so that you can quickly start experimenting. Figure 12-11 shows an example of VisualAge for Java 4.0 with our sample classes loaded.
378
The classes to start with are the two applets Auctioneer and AuctionCustomer, inheriting from AuctionUser. In AuctionUser youll find the common behavior of the two subclasses.
Scenario
If you want to get an idea of an auction scenario, see Appendix C, Sample auction scenario on page 381, where we walk you through an auction scenario and show screen captures of what is going on in the user interface for each step.
379
380
Appendix C.
381
The scenario
We have defined some users and their roles as we step through the auction; see Table 12-2 for details. The auction is used to sell picture artwork to interested customers all over the world.
Table 12-2 User roles
User Volker Juergensen Tom Talbert Jim Jackson Frank Furrer Role and remarks Auctioneer Tom is really interested in the Arizona Sun picture. He logs into the auction directly with his userid and password. Jim is also an art fan, and interested in almost any expensive item. So he logs into the auction. Frank is hitting the Web site announcing the auction by accident, and enters anonymously first. Once in the auction, he becomes interested, logs in, and starts bidding. Sam is interested in all kinds of art, but has no money to buy. He is happy that he can attend the auction as an anonymous user.
Sam Smith
Preparation
The auction items are one classic picture, Arizona Sun by Laura Nielsen, and a prototype of a wearable PC. Some pictures have been prepared and the auctioneers application parameters were set up as shown in Example C-1.
Example C-1 Auctioneers applet parameters
... <APPLET ... > <param name=archive ...> <param name=DefaultImageURL value="default.jpg"> <param name=numItems value=2> <param name=Item1Title value="Arizona Sun"> <param name=Item1URL value="AZSunc.jpg"> <param name=Item1Price value=1000> <param name=Item2Title value="Wearable PC"> <param name=Item2URL value="wearpclg.gif"> <param name=Item2Price value=500> </APPLET> ...
382
Remember, it is just to keep thing simple that we pass the auction item information as applet parameters. In the real world this information will be fetched as XML data from some kind of content management system, relational database, or similar. Also, the auction has been announced on a Web site and some invitations to good customers have been sent.
Entrance
Since we use simple static HTML pages, the auction participants use a URL like this to enter the auction:
http://<server>/<directory>/<filename>.html
Auction customers:
http://medion.lotus.com/auction/auctioncustomer.html
Tom and Jim use their user name and password for authentication, while Frank and Sam only type in their first name and click the Anonymous button of the login dialog, shown in Figure C-1.
After all users have entered, every user has a personalized view of the auction. The auctioneer has the most information available since he is monitoring the anonymous user list as well as the auditorium users (who are already logged in with their passwords). As you can see in Figure C-2 on page 384, the anonymous users are shown with the name they entered, but with an additional name part /Guest which identifies them as anonymous users.
383
You can also see that the auction has not yet been started, and that the Start button is available while the Call button (One-Two-Sold) is not. Authenticated users Tom and Jim use the AuctionCustomer applet, which lists all other users in the auditorium. Since the auction has not been started, the Bid button is disabled. Figure C-3 shows Toms browser window.
384
Anonymous users Frank and Sam use the same applet as the authenticated users do, but the section they entered is different, so the title of their awareness list is different. Their application is shown in Figure C-4.
Auction
Now we want to walk you through the auction itself. First, the auctioneer uses his send message function to send text messages to all auction participants - he wants to let the users know that the auction will start very soon.
385
To start the auction, the auctioneer clicks the Start button and the auction can begin.
The new auction status is reflected in the transcript of all auction participants. Additionally, now some other functions become available. For the auctioneer, the button labeled One, which is used to call the auction, is now activated.
The authenticated customers now can use the amount field and the Bid button to send a bid to the auctioneer.
386
Immediately after the bid was sent, the auctioneers application decides if the bid is accepted or not (for example, if the amount was lower than the current bid it would not be accepted). Here you can see Jims transcript window showing that his bid is accepted.
The current bid is not only visible on the transcript, it also is shown in the auction status area.
Sam, who is still watching the auction as an anonymous user, is now motivated and wants to bid. He clicks the Logout button, then he logs into the auction with his user name and password. The auctioneer sees how someone leaves the anonymous section and how Sam enters the auditorium section.
387
Sam is now able to send his bid to the auctioneer. He goes for 1300.
Sams bid appears on the transcript as all other users bids before.
The auctioneer now starts to call, so he clicks on the button labeled One. The call becomes visible as the auction status, and on the transcript.
388
But Jim Jackson bids again for 1400. The status is switching back.
Now some time goes on, and no new bids come in. The auctioneer calls One, Two, by clicking the appropriate buttons.
Finally, the auctioneer clicks on the button now labeled Sold. The transaction appears on the transcript.
389
The auctioneer can still use the message window to send messages to the auction users.
Finally, the auctioneer presses the button labeled Stop. The auction now moves on to the next item.
The same bidding sequence goes on again. This concludes our walkthrough of the sample scenario.
390
Appendix D.
Sametime portlets
Attention:
The contents of this appendix are based on WebSphere Portal Server 2.1, and will not work on newer versions of WebSphere Portal Server without modification. The newer releases of the WebSphere Portal Server, version 4.1 and above, have an entirely new Portlet API. Additionally, they have a built-in collaborative components API, which allows one to more easily create Sametime-aware portlets. Redbooks about the new technologies in WebSphere Portal 4.1 are available. See http://www.redbooks.ibm.com for more information. Sametime capabilities can be added to a portal to offer the user of the portal awareness and real-time communication capabilities. In this appendix we include two Sametime portlet examples for the WebSphere Portal Server. These samples are courtesy of Christian Steege of IBM Switzerland. Christian is a co-author of the redbook Domino and WebSphere Together, SG24-5955. The samples illustrate: Opening the Java Sametime Connect client by token (thus not requiring the user to authenticate again).
391
Launching a PlaceAwareness applet that makes the users enter a specified place and then displays all users in that place.
Note: For the purpose of this discussion, we assume knowledge of WebSphere Portal Server, or at least portlets in general, as well as some Lotus Domino knowledge.
Authentication by token
The Sametime portlet applications log into Sametime by token. They get the token from a Domino database on the Sametime server. That Domino database is a modified version of the stautht.nsf we describe in 7.6.1, Integrating with a Domino application on page 164. A form has been added to the Domino database that allows an authenticated user to get his or her token by the HTTP command:
http://<sametime server>/stapp.nsf/GetUserAndToken?OpenForm
This URL returns a page from which the token can be parsed out.
392
393
import org.apache.jetspeed.portlets.*; import org.apache.jetspeed.portletcontainer.*; import org.apache.jetspeed.portlet.service.ContentAccessService; // IBM SSO Classes import com.ibm.wps.sso.*; public class CallSametimeConnect extends AbstractPortlet { private private private private private String String String String String m_sSTServerName = ""; m_sSTDbPath = ""; m_sSTForm = ""; m_sSTPlace = ""; m_sUser = "";
public void doView(PortletRequest req, PortletResponse res) throws PortletException, IOException { PrintWriter out = res.getWriter(); // add userid and password to request String sBasicAuth = getBasicAuth (req); String sSTUserAndToken = getSTUserAndToken(sBasicAuth); String sURL = "http://" + m_sSTServerName + "/" + m_sSTDbPath + "/" + m_sSTForm + "?OpenForm"; if (!sSTUserAndToken.equals ("")) sURL = sURL.concat ("&" + sSTUserAndToken); // include Java script that opens the applet window this.getPortletConfig().getContext().include ("/sametime/openstwin.jsp", req, res); // print applet tags and link to open the st window out.println ("<SCRIPT Language=\"JavaScript\">"); out.println ("var sURL = \"" + sURL + "\";"); if (!sSTUserAndToken.equals("")) { out.println ("openSTClient();"); } out.println ("</SCRIPT>"); out.println ("<B><I><FONT FACE=\"Arial,Helvetica\" COLOR=\"#3333FF\" SIZE=-1>"); out.println ("<A HREF=\"javascript:openSTClient();\">Launch Now</A>"); } private String getBasicAuth(PortletRequest req) { String sAuthUser = getUserLogin (req, false); String sAuthPw = getUserLogin (req, true);
394
if (sAuthUser == null || sAuthPw == null) { return ""; } else { String sAuth = sAuthUser + ":" + sAuthPw; sAuth = com.ibm.ejs.security.util.Base64Coder.base64Encode(sAuth); sAuth = "Basic " + sAuth; return sAuth; } } private String getSTUserAndToken(String sBasicAuth) { try { if (sBasicAuth.equals ("")) { return ""; } else { URL url = new URL("http://" + m_sSTServerName + "/" + m_sSTDbPath + "/GetUserAndToken?OpenForm"); HttpURLConnection con = (HttpURLConnection) url.openConnection(); // Set our Basic Auth Header con.setRequestProperty("authorization", sBasicAuth); con.connect(); // Get the Response Code String sMessage = con.getResponseMessage(); int iResCode = con.getResponseCode(); if (HttpURLConnection.HTTP_OK == iResCode) { Object objPage = con.getContent(); String sSTUserAndToken = ""; if (objPage != null) { BufferedInputStream bufIn = new BufferedInputStream(con.getInputStream()); int c; while ((c=bufIn.read()) >= 0) { if (!sSTUserAndToken.endsWith ("</STUserAndToken>")) { sSTUserAndToken = sSTUserAndToken.concat (new Character((char)c).toString()); if (sSTUserAndToken.endsWith ("<STUserAndToken>")) sSTUserAndToken = ""; } } bufIn.close(); } if (sSTUserAndToken.length() > 17) { sSTUserAndToken = sSTUserAndToken.substring(0, sSTUserAndToken.length() - 17); }
395
return sSTUserAndToken; } else { return ""; } } } catch (Exception e) { return ""; } } private String getUserLogin(PortletRequest req, boolean bPW) { try { if (req.getSession().getUser() == null) { return null; } else { UserImpl userCurrent = (UserImpl) req.getSession().getUser(); Set setUser = null; if (bPW) { setUser = userCurrent.getSubject().getPrincipals(Class.forName ("com.ibm.wps.sso.PasswordCredential")); PasswordCredential pwUser = null; if (setUser != null) pwUser = (PasswordCredential) setUser.toArray()[0]; if (pwUser == null) { return null; } else { return pwUser.getName(); } } else { setUser = userCurrent.getSubject().getPrincipals(Class.forName("com.ibm.wps.sso.UserDNPri ncipal")); UserDNPrincipal dnUser = null; if (setUser != null) dnUser = (UserDNPrincipal) setUser.toArray()[0]; if (dnUser == null) { return null; } else { String sName = dnUser.getName(); // name translation if (m_sUser.compareTo ("WPS") == 0) { // standard wps installation int iFrom = sName.indexOf ("="); int iTo = sName.indexOf (","); if (iFrom > -1 && iTo > -1 && iFrom < iTo) { sName = sName.substring (iFrom + 1, iTo); } } return sName;
396
} } } } catch (ClassNotFoundException e) { return ""; } } public void init(PortletConfig conf) throws UnavailableException { super.init (conf); m_sSTServerName = getPortletConfig().getAttribute("stserver"); m_sSTDbPath = getPortletConfig().getAttribute("stdbpath"); m_sSTForm = getPortletConfig().getAttribute("stform"); m_sUser = getPortletConfig().getAttribute("getuser"); if (m_sUser == null) m_sUser = "";
} }
397
Figure D-2 Place awareness portlet Example D-2 Place awareness portlet
// Java classes import java.io.*; import java.util.*; import java.net.*; // Jetspeed classes import org.apache.jetspeed.portlet.*; import org.apache.jetspeed.portlets.*; import org.apache.jetspeed.portletcontainer.*; import org.apache.jetspeed.portlet.service.ContentAccessService; // IBM SSO Classes import com.ibm.wps.sso.*; public class CallSametimeDomPage extends AbstractPortlet { private String m_sSTServerName = ""; private String m_sSTDbPath = ""; private String m_sSTForm = "";
398
private String m_sSTPlace = ""; private String m_sUser = ""; public void doView(PortletRequest req, PortletResponse res) throws PortletException, IOException { PrintWriter out = res.getWriter(); // add userid and password to request String sAuth = getBasicAuth (req); // print applet tags out.println (getAppletTags (sAuth)); } private String getAppletTags(String sBasicAuth) { try { URL url = new URL("http://" + m_sSTServerName + "/" + m_sSTDbPath + "/" + m_sSTForm + "?OpenForm&Place=" + m_sSTPlace); HttpURLConnection con = (HttpURLConnection) url.openConnection(); // Set our Basic Auth Header con.setRequestProperty("authorization", sBasicAuth); con.connect(); // Get the Response Code String sMessage = con.getResponseMessage(); int iResCode = con.getResponseCode(); if (HttpURLConnection.HTTP_OK == iResCode) { Object objPage = con.getContent(); String sAppletTags = ""; if (objPage != null) { BufferedInputStream bufIn = new BufferedInputStream(con.getInputStream()); int c; while ((c=bufIn.read()) >= 0) { if (!sAppletTags.toLowerCase().endsWith ("</applet>")) { sAppletTags = sAppletTags.concat (new Character((char)c).toString()); if (sAppletTags.toLowerCase().endsWith ("<applet")) sAppletTags = "<applet"; } } bufIn.close(); } return sAppletTags; } else {
399
return "<P>Unable to connect!, HTTP Response Code = " + iResCode +", HTTP Response Message = \"" + sMessage + "\"<BR>"; } } catch (Exception e) { return "<P>Exception: " + e.toString() + "<BR>"; } } private String getBasicAuth(PortletRequest req) { String sAuth = getUserLogin (req, false) + ":" + getUserLogin (req, true); sAuth = com.ibm.ejs.security.util.Base64Coder.base64Encode(sAuth); sAuth = "Basic " + sAuth; return sAuth; } private String getUserLogin(PortletRequest req, boolean bPW) { try { UserImpl userCurrent = (UserImpl) req.getSession().getUser(); Set setUser = null; if (bPW) { setUser = userCurrent.getSubject().getPrincipals(Class.forName ("com.ibm.wps.sso.PasswordCredential")); PasswordCredential pwUser = null; if (setUser != null) pwUser = (PasswordCredential) setUser.toArray()[0]; if (pwUser == null) { return ""; } else { return pwUser.getName(); } } else { setUser = userCurrent.getSubject().getPrincipals(Class.forName("com.ibm.wps.sso.UserDNPri ncipal")); UserDNPrincipal dnUser = null; if (setUser != null) dnUser = (UserDNPrincipal) setUser.toArray()[0]; if (dnUser == null) { return ""; } else { String sName = dnUser.getName(); // name translation if (m_sUser.compareTo ("WPS") == 0) { // standard wps installation int iFrom = sName.indexOf ("="); int iTo = sName.indexOf (","); if (iFrom > -1 && iTo > -1 && iFrom < iTo) { sName = sName.substring (iFrom + 1, iTo); }
400
} return sName; } } } catch (ClassNotFoundException e) { return ""; } } public void init(PortletConfig conf) throws UnavailableException { super.init (conf); m_sSTServerName = getPortletConfig().getAttribute("stserver"); m_sSTDbPath = getPortletConfig().getAttribute("stdbpath"); m_sSTForm = getPortletConfig().getAttribute("stform"); m_sSTPlace = getPortletConfig().getAttribute("stplace"); m_sUser = getPortletConfig().getAttribute("getuser"); if (m_sUser == null) m_sUser = ""; } }
PlaceAwarenessList applet
In Example D-3 you can see the source code for the Sametime applet that is launched by the portlet shown in Example D-2 on page 398. The applet enters the user into the specified place and shows a list of all users in that place.
Example D-3 PlaceAwarenessList applet
import java.applet.*; import java.awt.*; import java.awt.event.*; import import import import import import import com.lotus.sametime.core.comparch.STSession; com.lotus.sametime.core.comparch.DuplicateObjectException; com.lotus.sametime.awarenessui.placelist.PlaceAwarenessList; com.lotus.sametime.awarenessui.av.AVController; com.lotus.sametime.community.*; com.lotus.sametime.places.*; com.lotus.sametime.core.constants.EncLevel;
401
STSession m_sesST; String m_sPlaceName = ""; PlaceAwarenessList m_stPlaceAwarenessList; Place m_stPlace; m_bLogin = false;
public void destroy() { if (m_bLogin) { CommunityService stComm = (CommunityService) m_sesST.getCompApi(CommunityService.COMP_NAME); m_stPlace.leave(0); stComm.logout(); m_sesST.stop(); m_sesST.unloadSession(); } } public void init() { try { // generate a new session with a unique name System.out.println ("starting Place Awareness List " + this); m_sesST = new STSession("Place Awareness List " + this); // Call the session to load all available components m_sesST.loadAllComponents(); m_sesST.start(); setLayout(new BorderLayout()); // create the new list view m_stPlaceAwarenessList = new PlaceAwarenessList(m_sesST, true); add(m_stPlaceAwarenessList, BorderLayout.CENTER); m_sPlaceName = getParameter ("placeName"); // The default right-click menu for the awareness list does not // give audio & video as options. We can change that by // changing the controller to a different one. AVController avController = new AVController(m_stPlaceAwarenessList.getModel()); m_stPlaceAwarenessList.setController(avController); // login to the community login();
402
} catch(DuplicateObjectException e) { // This exception is thrown if an STSession with the same // name has already been created. System.out.println ("Duplicate Session!!"); e.printStackTrace(); } } /** * Login event. */ public void loggedIn(LoginEvent event) { //Get refernce to the place service PlacesService placesService = (PlacesService) m_sesST.getCompApi(PlacesService.COMP_NAME); //Create a new place object //System.out.println ("Enter place " + m_sPlaceName); m_stPlace = placesService.createPlace(m_sPlaceName, m_sPlaceName, EncLevel.ENC_LEVEL_RC2_40, 0); //bind the list to the specified place. m_stPlaceAwarenessList.bindPlace(m_stPlace); //enable enter/leave button //m_button.setEnabled(true); m_stPlace.enter(); m_bLogin = true; } /** * Logout event */ public void loggedOut(LoginEvent event) { } /** * Login to the community using the user name and password * parameters from the html. */ private void login() { // get a reference to the community service. We use the session // object which contains a reference to all the components that // were loaded to get a reference to the community service. CommunityService stComm = (CommunityService)
403
m_sesST.getCompApi(CommunityService.COMP_NAME); // register a listener to the login/logout events. stComm.addLoginListener(this); // login to the community //System.out.println (getParameter("loginName") + "/" + getParameter("loginToken")); stComm.loginByToken(getCodeBase().getHost(), getParameter("loginName"), getParameter("loginToken")); // Wait for the loggedin() event to enter the place } }
404
Sample files
The samples described in this appendix are available for download. See Appendix E, Additional Web material on page 407 for instruction on how to get the samples included in the 6666portlets.zip file. Table D-1 describes the files in 6666portlets.zip
Table D-1 Content of 6666portlets.zip
File name stconnect.par Description Portlet for Sametime Java Connect. It contains the Sametime toolkit jar, an XML config file, and the JSP to launch the connect client in a separate window. Portlet for PlaceAwareness applet. It contains the Sametime toolkit jar and an XML config file. JSP for launching Sametime Java Connect. It is also included with stconnect.par. Domino database to provide the Sametime token. Is also contains the Sametime applets and forms to launch those applets. Java source for the Sametime Java Connect portlet. Java source for the Sametime Place awareness portlet. Java source for the Sametime Place awareness applet.
405
406
Appendix E.
6666auctionapp.zip
6666auction-javadoc.zip
407
Description This zip file contains the Java source for the Auction house sample in HTML format that can be viewed with a Web browser. This zip file contains the final sample from Chapter 7, Customized chat UI applet on page 137, both in source and executable format. This zip file contains the resulting Domino database embedding a Sametime applet as described in 7.6.1, Integrating with a Domino application on page 164. This zip file contains the source for the sample described in Chapter 9, A complex meetings sample on page 207, as well as a compiled version of the sample. This zip file contains a compiled version of the sample described in Chapter 10, Using the C++ toolkit in Win32 programs on page 297. This zip file contains the Visual Basic source for the three steps we go through in the sample in Chapter 11, The COM toolkit on page 309. This zip file contains the two portlets described in Appendix D, Sametime portlets on page 391.
6666custui.zip
6666custuidomino.zip
6666cpp-meeting.zip
6666win32.zip
6666com.zip
6666portlets.zip
Select the Additional materials and open the directory that corresponds with the redbook form number, SG246666.
408
Related publications
The publications listed in this section are considered particularly suitable for a more detailed discussion of the topics covered in this redbook.
IBM Redbooks
For information on ordering these publications, see How to get IBM Redbooks on page 410.
Lotus Sametime 2.0 Deployment Guide, SG24-6206 B2B Collaborative Commerce with Sametime, QuickPlace and WebSphere Commerce Suite, SG24-6218 Applying the Patterns for e-business to Domino and WebSphere Scenarios, SG24-6255 Lotus Mobile and Wireless Solutions, SG24-6525 Customizing QuickPlace, SG24-6000 Domino and WebSphere Together, SG24-5955 COM Together with Domino, SG24-5670 Lotus Domino Release 5.0: A Developer's Handbook, SG24-5331 iNotes Web Access Deployment and Administration, SG24-6518 Inside the Lotus Discovery Server, SG24-6252 XML Powered by Domino How to use XML with Lotus Domino, SG24-6207 Using VisualAge for Java to Develop Domino Applications, SG24-5424 Connecting Domino to the Enterprise Using Java, SG24-5425 Performance Considerations for Domino Applications, SG24-5602 Using Domino Workflow, SG24-5963
409
The developers corner of the Sametime Web site. Here you can download toolkits, beta version, browse technical documents, and participate in the Sametime developers discussion forum.
http://www.lotus.com/sametimedevelopers
Lotus Developer Network is Lotus primary destination for the latest developer information and resources. It contains articles about new and current technologies, along with relevant tips and techniques to help you build dynamic collaborative e-business applications.
http://www.lotus.com/developer/
Notes.net from Iris, the developers of Notes and Domino, is a technical Web site with discussion forums, documentation, and the Webzine Iris Today, with many good articles about technical details of Domino.
http://notes.net/
The IBM developerWorks Web site is designed for software developers, and features links to a host of developer tools, resources, and programs.
http://ibm.com/developer/
Lotus Supports Web site - Search using keywords or browse the Lotus Knowledge Base and locate helpful and informative tech notes and technical papers for the entire Lotus Product family. This source of information contains the latest technical information updated hourly.
http://support.lotus.com/
Entry point to information about the IBM WebSphere software platform for e-business:
http://ibm.com/websphere/
Also download additional materials (code samples or diskette/CD-ROM images) from this Redbooks site. Redpieces are Redbooks in progress; not all Redbooks become Redpieces and sometimes just a few chapters will be published this way. The intent is to get the information out much quicker than the formal publishing process allows.
410
Related publications
411
412
Index
A
Abstract Window Toolkit See AWT Access list place 56 stage section 56 Activity adding to place 286 in place 53 managing 232 ADA compliance 11 Adapter 26 Add Tools dialog 279 Anonymous login 31 Anonymous users deployment considerations 371 Applet connections over Internet 372 deployment considerations 163 entering a Sametime place 95 passing token 183 running in VisualAge for Java 86 subclass from STApplet 372 with Sametime code 89 Application sharing 38 Architecture C++ toolkit 200 complex meetings sample 212 places 49 Sametime 18 Attribute types 46 Attributes adding to place 286 in auction house sample 129 in place 54 listening for changes 65 setting in place 64 Auction house sample application flow 107 bid from cell phone 134 changing attributes 129 class diagram 115 event flows 124 event listeners and adapters 117 implementation 114 Java2HTML 377 JavaDoc 376 objects in application 106 overview 102 real life model 102 requirements 104 scenario 381 sending text/data 128 the client applications 110 user interface classes 119 working with 375 Audio activity 233 Audio over IP 38 Audio/Video wizard 241 Authentication 31 by token 189, 392 Sametime Links toolkit 364 Automatic nicknames 35 Awareness 4 attributes 32 place-based 51, 360 Awareness service 32 AWT 37
B
Broadcast 39 See also Streamed media Buddy list sample instant messaging type supported 347
C
Cell phone as Sametime client 134 Chat 4, 276 customizing window 138 Chat activity 233 Chat meeting 209 Sametime Links toolkit 359 window 222 Chat room Sametime Links toolkit 361
413
chat window Sametime Links toolkit 358 Class diagram for auction house sample 115 Classpath for JDK 74 in VisualAge for Java 82 Client toolkits See Toolkits Codebase Sametime Links toolkit 364 Communication in place 54 Community service 31 Community services figure 29 COMP_NAME 41 Components COMP_NAME 41 loading 40 STBase 40 Controller 20 See also Model-View-Controller Core types 42 attributes 46 importing 42 STAttribute 46 STExtendAttribute 46 STGroup 45 STId 43 STLoginId 43 STObject 44 STObjectImpl 44 STServer 45 STUser 45 STUserInstance 46 CustomizeChatUI how to enable in other components 182 Customized chat UI sample 137
Domino 36 LDAP 36 Directory service 35 Domino as applet container 164 enabling database for Sametime applet 171 Sametime Discussion template 169 Domino directory 36
E
Error handling 98 Event definition 23 Event-based programming 22 adapter 26 event 23 listener 23 subscription 25 Existential attribute 47 Extend versus implement 28 Extranet support Sametime Links toolkit 357
G
Group 45
H
Heavy attribute 47 HTML chat window 358 Sametime Links toolkit 355 source code for auction house sample 378 HTTP tunneling Sametime 372
I
IBM VisualAge for Java and Sametime Java toolkit 79 classpath 82 exporting code 86 importing Sametime samples 81 object hierarchy view 116 passing parameters to program 84 running applet 86 setup 79 source code for auction house sample 378
D
Data transfer instant messaging 374 Deployment considerations 371 Deployment considerations applets 163 Directory
414
IM See Instant messaging Image loading from jar file 172 Implement versus extend 29 Infrastructure for anonymous users 371 Inner class 27 Instant messaging 5 and data transfer 374 type supported by client 347 Instant messaging service 34 Interface 25 definition 24 implement versus extend 29 using LoginListener 94 Internet and Sametime applets 372 IP audio and video 38
L
Late binding Sametime COM toolkit 315 LDAP 36 Listener 23 See also Nested class definition 25 in auction house sample 117 place messages 55 using LoginListener 94 Load time toolkit comparison 10 Login adding listener 41 anonymously 31 by password 31 by token 31, 34 Sametime community 40 STBase 40 using LoginListener interface 94 Lookup service 33 LTPA Sametime Links toolkit 364
J
Jar file 79 loading image from 172 Java extend and implement 28 inner class 27 nested class 27 Java Development Kit See JDK Java security policy 77 Java2HTML auction house sample 377 JavaDoc auction house sample 376 JavaScript accessing token 189 API in Sametime Links toolkit 357 Sametime Links toolkit 355 JDK classpath 75 compiling and running 76 installing 74 path 75 policy file 78
M
Meeting factory services 38 Meeting invitation dialog 260 Meeting room client 217 URL to launch 292 Meeting services 37 figure 29 Message loop Win32 program 304 MFC Sametime C++ toolkit library 304 versus Win32 298 Microsoft Foundation Classes See MFC Model 20 See also Model-View-Controller Model-View-Controller 20 MRC See Meeting room client
N
Names service 35 Nested class 27 Nicknames 35
Index
415
O
Online meeting services 5
P
Path for JDK 74 Permissions in place 55 Persistent place 52 Place See also Place patterns activity 53 adding activities 232 adding activity 286 adding attributes 286 attributes 54 common pitfalls 132 communication 54 community span 56 creation modes 267 figure of places model 51 how to throw a user out 133 joining existing 267 name of 133 online auction Java sample 101 overview 50 permissions list 55 persistent 52 restricting access 56 scalability 56 section 53 setting attributes 64 stage section 53 text/data messages 54 Place patterns 57 changing section 61 entering a place 58 identifying sections 60 list of users in a section 63 listening for changed attributes 65 listening for messages sent to you 67 listening for place events 58 listening for section events 62
listening to users in section 63 sending messages 66 setting attributes 64 Places architecture 49 in auction house sample 127 Places service 33 Policy file for JDK 78 in VisualAge for Java 85 Portlet Java Sametime Connect 391 PlaceAwareness 392 Sametime 391 WebSphere Portal Server 391 Post service 36 Proxy objects 19 Proxy PAC file support 372 Public group 45
R
Redbooks Web site 410 Contact us xvi Resolve name 45 Restricting access to place 56 Runtime components Sametime Links toolkit 363
S
Sametime architecture 18 authentication 31 direct socket connection 372 enabling Domino database 170 events 18 HTTP connection 372 in Domino application 164 meeting center 38 Model-View-Controller 21 portlets 391 proxy objects 19 See also Toolkit services services 29 streamed media 38 toolkits - See Toolkits what is it 4 Sametime C++ toolkit available services 202
416
compared 11 in Win32 programs 297 layered architecture 200 libraries for MFC/Win32 304 message loop in Win32 program 304 overview 8 package 203 target platform 9 wmain 304 working with 199 Sametime COM toolkit adding tolkit reference to project 313 awareness service 311 community service 311 compared 11 getting it 312 instant messaging service 311 JavaScript in IE 310 late binding 315 lookup service 311 overview 8, 310 target platform 9 Visual Basic 310 Sametime Discussion template 169 Sametime Everyplace 134 Sametime Java toolkit and IBM VisualAge for Java 79 compared 11 compiling and running with JDK 76 customized chat window 138 enabling use of customized chat window 182 error constants 98 how to get 71 overview 8 services used in auction house sample 114 target platform 9 writing applets 89 Sametime Links toolkit adding a link 365 authentication considerations 364 chat rooms 361 codebase 364 compared 11 enabling Web application 363 features 356 overview 7, 355 runtime components 363 target platform 9 Sametime Meeting client
launching from C++ program 207 Sample auction house (place-based) 106 awareness (COM toolkit) 320 Buddy List (COM toolkit) 310, 342 complex meetings sample 207 CustomizeChatUI (user interface) 137 Java Sametime Connect portlet 391 login (COM toolkit) 316 online assistance (STLinks) 366 passing token between applets 183 place awareness portlet 392 PlaceQuickStart (basic place-based) 95 QuickStart (basic applet) 90 RedCustumizeChatUI (storage service) 173 Win32Status 298 Scalability place 56 Scenario auction house 381 Section 53 identifying 60 listening to users 63 moving to other sections 61 restricting access 56 setting attributes 64 stage 53 Service layer 200 Session creating 40 Single sign on Sametime Links toolkit 364 Skills required toolkit comparison 10 Stage section 53, 273 restricting access 56 STApplet 372 STAttribute 46 STBase 40 STError 98 STExtendAttribute 46 STGroup 45 STId 43 STLoginId 43 STObject 44 STObjectImpl 44 Storage service 34 Streamed media 38 STServer 45
Index
417
T
Text/data messages in auction house sample 128 in place 54 Thread-safe C++ toolkit 201 Token expiry time 34 generating 290 passing between applets 183 SametimeSecretGenerator agent 34 use in portlet sample 392 Token service 34 Toolkit services activity 57 application sharing 38 awareness 32, 311, 358 broadcast 39 community 31, 311 core types 42 directory 35 figure 29 general features 30 instant messaging 34, 311, 358 loading components 40 lookup 33, 311 meeting 37 meeting factory 38 names 35 places 33 PlacesAdmin 57 post 36, 246 storage 34, 173 streamed media 38 token 34, 184, 290 used in auction house sample 114 whiteboard 38 Toolkits 7 ADA compliance 12 adapters 26 C++ 8 COM 8
comparison 11 features supported 9 interfaces 24 Java 8 load time 10 overview 7 Sametime Links 7 skills required 10 target platforms 9 which to use 8 Transport layer 200 Type instant messaging 347
U
URL for Sametime Meeting room client 292 User listening for messages in place 67 setting attributes in place 64 User interface classes in auction house sample 119
V
VBA Sametime COM toolkit 8, 310 Video activity 233 Video over IP 38 View 20 See also Model-View-Controller Virtual place See Place Visual Basic adding COM toolkit reference to project 313 Sametime COM toolkit 8, 310 VisualAge for Java policy file 85
W
Web application enabling with Sametime Links toolkit 363 WebSphere Portal Server Sametime portlet 391 Whiteboard service 38 Win32 message loop 304 Sametime C++ toolkit 297
418
Index
419
420
Back cover
BUILDING TECHNICAL INFORMATION BASED ON PRACTICAL EXPERIENCE IBM Redbooks are developed by the IBM International Technical Support Organization. Experts from IBM, Customers and Partners from around the world create timely technical information based on realistic scenarios. Specific recommendations are provided to help you implement IT solutions more effectively in your environment.
SG24-6666-00
ISBN 0738423920