Customizing the IBM Sametime Business Card: Difference between revisions

From Wiki
(Created page with "I migrated the article "Customizing the IBM Sametime Business Card" from IBM developerWorks, after the sale of Sametime to HCL. =Summary= Having access to information about coworkers in day-to-day work should be quick and easy. This article describes, in a practical way, how to make such information available through the IBM Sametime Connect Client and the IBM Sametime Web Client (Sametime Proxy Server). =Introduction= Our story begins with a multinational company th...")
 
 
(5 intermediate revisions by the same user not shown)
Line 88: Line 88:
Despite the ease of using the Sametime Administration Console, we are limited to the available fields. Moreover, not all user information is stored in a single repository, such as an LDAP directory, due to performance reasons. For example, user photos.
Despite the ease of using the Sametime Administration Console, we are limited to the available fields. Moreover, not all user information is stored in a single repository, such as an LDAP directory, due to performance reasons. For example, user photos.


== 1.2. O subsistema de Cartão de Visita ==
== 1.2. The Business Card Subsystem ==


O subsistema de Cartão de Visita do IBM Sametime busca as informações dos repositórios configurados no arquivo UserInfoConfig.xml.
The Business Card subsystem of IBM Sametime retrieves information from the repositories configured in the UserInfoConfig.xml file.


Estes repositórios podem ser:
These repositories can be:


* Um diretório LDAP
* An LDAP directory
* Um aplicação Notes
* A Notes application
* Uma classe Java, que por sua vez busca informações em uma base SQL ou em um ERP/SAP.
* A Java class, which in turn retrieves information from a SQL database or an ERP/SAP system.


A interface (API) que faz acesso a esses repositório que é chamada de BlackBox.
The interface (API) that accesses these repositories is called the BlackBox.


O processo de busca das informações pelo Cliente no repositório é descrito abaixo:
The process of retrieving information by the Client from the repository is described below:


1) o Cliente Sametime faz uma requisição HTTP (GET) a um servidor Sametime Community.
# The Sametime Client makes an HTTP (GET) request to a Sametime Community server.
 
# When the request reaches the Sametime/Domino HTTP server, Domino redirects the request to the UserInfoServlet servlet.
2) Quando a requisição chega no servidor HTTP do Sametime/Domino, o Domino redireciona o request para o servlet UserInfoServlet
# The UserInfoServlet servlet reads the UserInfoConfig.xml file, which contains the connection information for the repositories.
 
# The servlet queries each repository to retrieve the information.
3) O servlet UserInfoServlet, por sua vez lê o arquivo UserInfoConfig.xml, a qual tem as informações de conexão aos repositórios.
# The data from the repositories is returned to the UserInfo servlet.
 
# The servlet combines the responses from these repositories and converts them into an XML document.
4) O servlet consulta cada repositório em busca das informações
# The HTTP server sends the XML document to the client, which extracts the data and displays it on the client.
 
5) Os dados dos repositórios são retornadas ao servlet UserInfo
 
6) O servlet combina as respostas destes repositórios e converte em um documento XML.
 
7) O servidor HTTP envia o documento XML para o cliente, que extrai os dados e apresenta no cliente.


[[File:SametimeCustomizando03.png]]
[[File:SametimeCustomizando03.png]]


Figura 3: Configuração do Cartão de Visita no Sametime System Console
Figure 3: Business Card Configuration in the Sametime System Console


== 1.3. Adicionando atributos através do UserInfoConfig.xml ==
== 1.3. Adding Attributes via UserInfoConfig.xml ==


O arquivo UserInfoConfig.xml permite uma maior flexibilidade na customização dos dados apresentados no Cartão de Visita.  
The UserInfoConfig.xml file allows greater flexibility in customizing the data displayed on the Business Card.  


O arquivo é gerado durante a instalação do Servidor Sametime Community e fica localizado no seguinte diretório (conforme a plataforma):
The file is generated during the installation of the Sametime Community Server and is located in the following directory (depending on the platform):


'''MS Windows''':  
'''MS Windows''':  


  <DOMINO_INSTALL_DIR>\UserInfoConfig.xml.
  <DOMINO_INSTALL_DIR>\UserInfoConfig.xml
 
  Exemplo: C:\IBM\Domino\UserInfoConfig.xml.
  Example: C:\IBM\Domino\UserInfoConfig.xml


'''Linux/AIX''':  
'''Linux/AIX''':  


  <DOMINO_DATA>\UserInfoConfig.xml
  <DOMINO_DATA>\UserInfoConfig.xml
 
  Exemplo: /local/notesdata
  Example: /local/notesdata


Ao abrirmos o arquivo UserInfoConfig.xml, você poderá observar, basicamente, duas seções:
When opening the UserInfoConfig.xml file, you will basically see two sections:


Na seção '''<Storage type="LDAP">''', temos as informações de conexão ao servidor LDAP  
In the '''<Storage type="LDAP">''' section, we have the connection information to the LDAP server: 


  <Storage type="LDAP">  
  <Storage type="LDAP">  
Line 148: Line 142:
     BaseDN="" Scope="2"  
     BaseDN="" Scope="2"  
     SearchFilter="(& (objectclass=organizationalPerson)(|(cn=%s)(givenname=%s)(sn=%s)(mail=%s)))" />  
     SearchFilter="(& (objectclass=organizationalPerson)(|(cn=%s)(givenname=%s)(sn=%s)(mail=%s)))" />  
  <!-- Add another StorageDetails tag to support another ldap server. The listing order implies the searching order -->  
  <!-- Add another StorageDetails tag to support another LDAP server. The listing order implies the searching order -->  
  <!-- Scope: 0=OBJECT_SCOPE 1=ONELEVEL_SCOPE 2=SUBTREE_SCOPE-->  
  <!-- Scope: 0=OBJECT_SCOPE 1=ONELEVEL_SCOPE 2=SUBTREE_SCOPE-->  


Listagem 1: Seção com as informações de conexão ao LDAP
Listing 1: Section with LDAP connection information


Enquanto na seção '''Details''', temos o mapeamento entre os Atributos do Sametime e do LDAP
While in the '''Details''' section, we have the mapping between Sametime attributes and LDAP fields:


     <Details>  
     <Details>  
Line 161: Line 155:
       <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/>  
       <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/>  
       <Detail Id="Telephone" FieldName="telephoneNumber" Type="text/plain"/>  
       <Detail Id="Telephone" FieldName="telephoneNumber" Type="text/plain"/>  
       <Detail Id="Company" FieldName="ou" Type="text/plain" />  
       <Detail Id="Company" FieldName="ou" Type="text/plain"/>  
       <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg" />  
       <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg"/>  
     </Details>  
     </Details>  
   </Storage>  
   </Storage>  
Line 171: Line 165:
  </ParamsSets>  
  </ParamsSets>  


Listagem 2: Seção com o mapeamento dos atributos
Listing 2: Section with attribute mappings
 
Vamos começar então o procedimento para adicionar novos campos:
 
Procedimento:


Antes de iniciarmos devemos por precaução realizar um backup do arquivo UserInfoConfig.xml
We can now start the procedure to add new fields:


1) Edite o arquivo '''UserInfoConfig.xml'''
=== Procedure ===


2) Localize a seção '''Details'''
Before starting, as a precaution, make a backup of the UserInfoConfig.xml file.


3) Localize a entrada '''Telephone''' e vamos incluir o número do Celular.
# Edit the '''UserInfoConfig.xml''' file.
# Locate the '''Details''' section.
# Locate the '''Telephone''' entry and add the mobile number:


  <Detail Id="Telephone" FieldName="telephoneNumber,'''mobile'''" Type="text/plain" '''DisplaySeparator=" / "''' />  
  <Detail Id="Telephone" FieldName="telephoneNumber,'''mobile'''" Type="text/plain" '''DisplaySeparator=" / "''' />  


4) Antes da tag '''</Details>''', vamos adicionar agora dois novos campos, como abaixo:
# Before the '''</Details>''' tag, add two new fields as follows:


  <Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/>
  <Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/>
  <Detail Id="City" FieldName="city" Type="text/plain"/>
  <Detail Id="City" FieldName="city" Type="text/plain"/>


5) Localize a seção ParamsSets e adicione os novos campos
# Locate the ParamsSets section and add the new fields:


  <ParamsSets>  
  <ParamsSets>  
Line 199: Line 191:
  </ParamsSets>  
  </ParamsSets>  


6) Salve e Feche o arquivo.
# Save and close the file.


O arquivo final fica da seguinte maneira:
The final file looks as follows:


   <Details>  
   <Details>  
       <Detail Id="MailAddress" FieldName="mail" Type="text/plain"/>  
       <Detail Id="MailAddress" FieldName="mail" Type="text/plain"/>  
       <Detail Id="Name" FieldName="cn" Type="text/plain"/>  
       <Detail Id="Name" FieldName="cn" Type="text/plain"/>  
       <Detail Id="Title" FieldName="title" Type="text/plain"cod/>  
       <Detail Id="Title" FieldName="title" Type="text/plain"/>  
       <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/>  
       <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/>  
       <Detail Id="Telephone" FieldName="telephoneNumber,'''mobile'''" Type="text/plain" '''DisplaySeparator=" / "''' />  
       <Detail Id="Telephone" FieldName="telephoneNumber,'''mobile'''" Type="text/plain" '''DisplaySeparator=" / "''' />  
       <Detail Id="Company" FieldName="ou" Type="text/plain" />  
       <Detail Id="Company" FieldName="ou" Type="text/plain"/>  
       <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg" />  
       <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg"/>  
       <Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/>  
       <Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/>  
       <Detail Id="City" FieldName="city" Type="text/plain"/>
       <Detail Id="City" FieldName="city" Type="text/plain"/>
Line 221: Line 213:
  </ParamsSets>  
  </ParamsSets>  


Listagem 3: Resultado das alterações sobre o arquivo UserInfoConfig.xml
Listing 3: Result of modifications to UserInfoConfig.xml


7) Reinicie o servidor Sametime Community
# Restart the Sametime Community Server.


Para testar o resultado da alteração do UserInfoConfig.xml, você pode verificar o resultado retornado pelo servlet UserInfoServlet.
To test the changes in UserInfoConfig.xml, you can check the result returned by the UserInfoServlet.


Em um navegador, entre com o endereço
In a browser, go to:


  <nowiki>http://&lt;Sametime_Server&gt;/servlet/UserInfoServlet?operation=3&amp;setid=2&amp;userid=&lt;Test_Account_Name&gt;</nowiki>
  <nowiki>http://&lt;Sametime_Server&gt;/servlet/UserInfoServlet?operation=3&amp;setid=2&amp;userid=&lt;Test_Account_Name&gt;</nowiki>


onde:
where:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Parâmetro !! Descrição
! Parameter !! Description
|-
|-
| operation || Operação a ser executada. Utilize o valor 3.
| operation || Operation to be performed. Use the value 3.
|-
|-
| setid || Conjunto de parâmetros que devem ser retornados, definidos na seção ParamsSets do arquivo UserInfoConfig.xml
| setid || Parameter set to be returned, defined in the ParamsSets section of UserInfoConfig.xml
|-
|-
| userid || Chave de pesquisa
| userid || Lookup key
|}
|}


Exemplo:
Example:


   <nowiki>http://stserver/servlet/UserInfoServlet?operation=3&amp;setid=2&amp;userid=uid=ebasso</nowiki>
   <nowiki>http://stserver/servlet/UserInfoServlet?operation=3&amp;setid=2&amp;userid=uid=ebasso</nowiki>


Onde o resultado
Where the result is:


  &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
  &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
Line 255: Line 247:
     &lt;field name=&quot;MailAddress&quot; type=&quot;&quot; error=&quot;'''UNAVAILABLE'''&quot;/&gt;
     &lt;field name=&quot;MailAddress&quot; type=&quot;&quot; error=&quot;'''UNAVAILABLE'''&quot;/&gt;
     &lt;field name=&quot;Name&quot; type=&quot;text/plain&quot;&gt;'''Enio Rubens Basso'''&lt;/field&gt;
     &lt;field name=&quot;Name&quot; type=&quot;text/plain&quot;&gt;'''Enio Rubens Basso'''&lt;/field&gt;
     &lt;field name=&quot;Title&quot; type=&quot;text/plain&quot;&gt;'''Especialista de TI'''&lt;/field&gt;
     &lt;field name=&quot;Title&quot; type=&quot;text/plain&quot;&gt;'''IT Specialist'''&lt;/field&gt;
     &lt;field name=&quot;Location&quot; type=&quot;&quot; error=&quot;'''UNAVAILABLE'''&quot;/&gt;
     &lt;field name=&quot;Location&quot; type=&quot;&quot; error=&quot;'''UNAVAILABLE'''&quot;/&gt;
     &lt;field name=&quot;Telephone&quot; type=&quot;text/plain&quot;&gt;'''61-3333-4444 / 61-9999-9999'''&lt;/field&gt;
     &lt;field name=&quot;Telephone&quot; type=&quot;text/plain&quot;&gt;'''61-3333-4444 / 61-9999-9999'''&lt;/field&gt;
Line 263: Line 255:
  &lt;/userinfo&gt;
  &lt;/userinfo&gt;


Listagem 4: Retorno do servlet
Listing 4: Servlet return
 
Como você pode ver, caso o valor de uma atributo no LDAP não esteja populado, o UserInfoServlet retorna  '''error=&quot;UNAVAILABLE&quot;'''.


= Adicionar campos provenientes do Sistema de RH da Empresa =
As you can see, if an attribute value in LDAP is not populated, UserInfoServlet returns '''error=&quot;UNAVAILABLE&quot;'''.


Para atender o segundo desafio do departamento de Marketing, o Administrador do Sametime, precisa buscar as informações que se encontram no Sistema de RH da Empresa.
= Adding Fields from the Company HR System =


Para evitar realizar consultas a todo momento no Sistema de RH, vamos criar um repositório local para armazenar as informações a serem apresentadas no Cartão de Visita do Sametime.
To address the second challenge from the Marketing department, the Sametime Administrator needs to retrieve information from the Company's HR System.


Para realizar esta tarefa precisamos realizar 3 atividades:
To avoid querying the HR System constantly, we will create a local repository to store the information to be displayed on the Sametime Business Card.


* Criar o repositório local
To perform this task, we need to complete 3 activities:
* Importar os dados do Sistema de RH
* Configurar o UserInfoConfig.xml.


Como pré requisito da tarefa, você vai precisar ter um Domino Designer instalado no seu computador, e configurado para acessar o servidor do Sametime Comunity. Utilize o usuario Administrador do Sametime para que nao tenha restricoes na criacao da database e de execucao no servidor.
* Create the local repository
* Import data from the HR System
* Configure UserInfoConfig.xml


== 2.1. Criando o repositório local ==
As a prerequisite, you need to have Domino Designer installed on your computer and configured to access the Sametime Community server. Use the Sametime Administrator account to avoid restrictions when creating the database and running operations on the server.


Vamos criar uma aplicacao Notes (NSF) para armazenar os dados.
== 2.1. Creating the Local Repository ==


1) Abra o Domino Designer
We will create a Notes application (NSF) to store the data.


2) Crie a nova base, atraves do menu '''File''' -&gt; '''Application''' -&gt; '''New'''
# Open Domino Designer
# Create a new database via the menu '''File''' -&gt; '''Application''' -&gt; '''New'''


Informe os seguintes campos
Fill in the following fields:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Campo !! Valor
! Field !! Value
|-
|-
| Server || <Nome do Servidor do Sametime>
| Server || &lt;Sametime Server Name&gt;
|-
|-
| Title || Secondary Book Store
| Title || Secondary Book Store
Line 303: Line 294:
| Server || Local
| Server || Local
|-
|-
| Template || Selecione '''-Blank-'''
| Template || Select '''-Blank-'''
|}
|}


Clique em '''OK'''
Click '''OK'''


[[File:SametimeCustomizando04.png]]
[[File:SametimeCustomizando04.png]]


Figura 4: Caixa de Dialogo para criar uma nova aplicação
Figure 4: Dialog Box for creating a new application


Vamos criar agora o formulário
=== Creating the Form ===


4) Crie o novo formulario, atraves do menu '''Create''' -&gt; '''Design''' -&gt; '''Form'''
# Create a new form via '''Create''' -&gt; '''Design''' -&gt; '''Form'''


{| class="wikitable"
{| class="wikitable"
|-
|-
! Campo !! Valor
! Field !! Value
|-
|-
| Name || person
| Name || person
Line 326: Line 317:
| Comment ||  
| Comment ||  
|-
|-
| Application || Secondary Book Store:\\<Nome do Servidor do Sametime>
| Application || Secondary Book Store:\\&lt;Sametime Server Name&gt;
|}
|}


Clique em '''OK'''
Click '''OK'''


5) Crie o campo chave, denominado '''uid'''. Atraves do menu '''Create''' -&gt; '''Design''' -&gt; '''Field'''.
# Create the key field named '''uid''' via '''Create''' -&gt; '''Design''' -&gt; '''Field'''


Informe no campo '''Name''', o valor '''uid''' , defina como tipo '''Text'''.
Enter '''uid''' for Name and set Type to '''Text'''.


[[File:SametimeCustomizando05.png]]
[[File:SametimeCustomizando05.png]]


Figura 5: Caixa de Dialogo para criar uma novo campo
Figure 5: Dialog box to create a new field


6) Vamos criar os demais campos usados neste artigo.
# Create the remaining fields used in this article:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Nome do Campo !! Tipo !! Descrição
! Field Name !! Type !! Description
|-
|-
| dpto || Text || Departamento
| dpto || Text || Department
|-
|-
| updated || Text || Data de atualização da foto
| updated || Text || Photo update date
|-
|-
| PhotoURL || Text || URL da Imagem
| PhotoURL || Text || Image URL
|-
|-
| Photo || Rich Text Lit || arquivo da imagem
| Photo || Rich Text Lit || Image file
|}
|}


Salve o formulario, atraves do menu '''File''' -&gt; '''Save'''.
Save the form via '''File''' -&gt; '''Save'''.


[[File:SametimeCustomizando06.png]]
[[File:SametimeCustomizando06.png]]


Figura 6: Novos campos criados
Figure 6: New fields created


Vamos criar a visao de indice
=== Creating the Index View ===


7) Crie uma nova visao, atraves do menu '''Create''' -&gt; '''Design''' -&gt; '''View'''
# Create a new view via '''Create''' -&gt; '''Design''' -&gt; '''View'''


Informe:
Fill in:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Campo !! Valor
! Field !! Value
|-
|-
| View Name || vwIndex
| View Name || vwIndex
Line 374: Line 365:
| View Type || Shared
| View Type || Shared
|-
|-
| Select Conditions || By Formula
| Selection Formula || By Formula
|-
|-
| Select Conditions || Select @All
| Select Conditions || Select @All
|}
|}


Clique em '''Save and Customize'''
Click '''Save and Customize'''


[[File:SametimeCustomizando07.png]]
[[File:SametimeCustomizando07.png]]


Figura 6: Caixa de Dialogo para criar visão
Figure 7: Dialog box to create a view


8) Clique na coluna '''#''', selecione '''Field''' e selecione '''uid'''.
# Click the column '''#''', select '''Field''', and choose '''uid'''.


[[File:SametimeCustomizando08.png]]
[[File:SametimeCustomizando08.png]]


Figura 7: Definindo o valor da coluna
Figure 8: Setting the column value


9) Na caixa de diálogo de propriedades, clique na segunda aba e em sort clique em '''Ascending'''.
# In the properties dialog, click the second tab and under Sort, select '''Ascending'''.


[[File:SametimeCustomizando09.png]]
[[File:SametimeCustomizando09.png]]


Figura 8: Definindo o tipo de ordenacao
Figure 9: Setting the sort order


Salve a visao, atraves do menu '''File''' -&gt; '''Save'''.
Save the view via '''File''' -&gt; '''Save'''.


Agora ja possuímos os uma repositório secundário local.
Now we have a local secondary repository.


== 2.2. Importando os dados do Sistema de RH ==
== 2.2. Importing Data from the HR System ==


O Sistema de RH da empresa fica armazenado em um banco de Dados DB2.
The Company HR System is stored in a DB2 database.


Nesta seção o vamos criar um agente na linguagem Java para buscar as informacoes da base de dados SQL , e popular o repositorio NSF local.
In this section, we will create a Java agent to retrieve information from the SQL database and populate the local NSF repository.


O nosso agente esta divido em 5 partes:
The agent is divided into 5 parts:


# Definição das variáveis
# Defining variables
# Inicialização do Driver JDBC do DB2
# Initializing the DB2 JDBC Driver
# Execução da query
# Executing the query
# Criação ou Atualização do documento no repositório NSF
# Creating or updating documents in the NSF repository
# Método principal
# Main method


'''Preparação''':
=== Preparation ===


Como preparação para o acesso ao DB2, devemos adicionar os drivers JDBC (tipo 4) do DB2.
To access DB2, we must add the DB2 JDBC (type 4) drivers.


Dentro do diretorio &lt;DOMINO_INSTALL_DIR&gt;\ibm-jre\jre\lib\ext, devemos copiar os arquivos:
Copy the following files into &lt;DOMINO_INSTALL_DIR&gt;\ibm-jre\jre\lib\ext:


* db2jcc.jar
* db2jcc.jar
Line 426: Line 417:
[[File:SametimeCustomizando10.png]]
[[File:SametimeCustomizando10.png]]


Figura 9: Configuracao dos drivers JDBC do DB2
Figure 10: DB2 JDBC driver configuration


'''Procedimento''':
=== Procedure ===


Crie o novo atraves do menu '''Create''' -&gt; '''Design''' -&gt; '''Agent'''
# Create a new agent via '''Create''' -&gt; '''Design''' -&gt; '''Agent'''


{| class="wikitable"
{| class="wikitable"
|-
|-
! Campo !! Valor
! Field !! Value
|-
|-
| Name || ImportPersonInfo
| Name || ImportPersonInfo
Line 444: Line 435:
| Type || Java
| Type || Java
|-
|-
| Application || Secondary Book Store:\\<Nome do Servidor do Sametime>
| Application || Secondary Book Store:\\&lt;Sametime Server Name&gt;
|}
|}
Clique em '''OK'''
Click '''OK'''


2) Em '''Basics''', defina '''On Schedule''' -&gt; '''Daily''', em Target defina '''None'''.
# In '''Basics''', set '''On Schedule''' -&gt; '''Daily''', and Target to '''None'''.


[[File:SametimeCustomizando11.png]]
[[File:SametimeCustomizando11.png]]


3) Em '''Security''', defina '''3. Allow restricted operations with full administration rights'''.
# In '''Security''', select '''3. Allow restricted operations with full administration rights'''.


[[File:SametimeCustomizando12.png]]
[[File:SametimeCustomizando12.png]]


4) Click em JavaAgent.java para criarmos o código'''.'''
# Click on JavaAgent.java to create the code.


[[File:SametimeCustomizando13.png]]
[[File:SametimeCustomizando13.png]]


5) Dentro da Classe '''JavaAgent''', devemos definir as contantes de conexao ao DB2, variaveis globais a classe, ...
# In the '''JavaAgent''' class, define the DB2 connection constants and global variables:


  // CONSTANTES
  // CONSTANTS
  private static final String JDBC_CLASS = &quot;com.ibm.db2.jcc.DB2Driver&quot;;  
  private static final String JDBC_CLASS = "com.ibm.db2.jcc.DB2Driver";  
  private static final String JDBC_URL = &quot;jdbc:db2://db2srv.empresax.com.br:50000/RHS&quot;;  
  private static final String JDBC_URL = "jdbc:db2://db2srv.empresax.com.br:50000/RHS";  
  private static final String JDBC_USERID = &quot;STUSER&quot;;  
  private static final String JDBC_USERID = "STUSER";  
  private static final String JDBC_PASSWORD = &quot;STUSER&quot;;  
  private static final String JDBC_PASSWORD = "STUSER";  
  private static final String UID_LOOKUP_VIEW_NAME = &quot;vwIndex&quot;;  
  private static final String UID_LOOKUP_VIEW_NAME = "vwIndex";  
  private static final String SQL =  
  private static final String SQL =  
  &quot;SELECT UID, DPTO &quot; +  
  "SELECT UID, DPTO " +  
  &quot;FROM EMPLOYEE &quot; +  
  "FROM EMPLOYEE " +  
  &quot;ORDER BY UID&quot;;  
  "ORDER BY UID";  
   
   
  // VARIAVEIS GLOBAIS A CLASSE
  // GLOBAL VARIABLES
  Connection con;  
  Connection con;  
  String uid = &quot;&quot;;  
  String uid = "";  
  String dpto = &quot;&quot;;
  String dpto = "";


6) Inicializacao do Driver JDBC do DB2
# Initialize the DB2 JDBC Driver:


  private void initDB() throws Exception {  
  private void initDB() throws Exception {  
  // Load the requested JDBC driver type
   Class.forName(JDBC_CLASS).newInstance();  
   Class.forName(JDBC_CLASS).newInstance();  
  con = DriverManager.getConnection(JDBC_URL, JDBC_USERID, JDBC_PASSWORD);  
con = DriverManager.getConnection(JDBC_URL, JDBC_USERID, JDBC_PASSWORD);  
  }
  }


7) Execucao da query
# Execute the query:
 
Para cada linha retornada pelo SELECT, vamos chamar o metodo '''updateOrCreateDocument'''.


  private void runMain(Database db) throws Exception {  
  private void runMain(Database db) throws Exception {  
   Statement stmt = con.createStatement();  
   Statement stmt = con.createStatement();  
   System.out.println(&quot;runQuery - start&quot;);  
   System.out.println("runQuery - start");  
   ResultSet rs = stmt.executeQuery(SQL);  
   ResultSet rs = stmt.executeQuery(SQL);  
   System.out.println(&quot;runQuery - end&quot;);  
   System.out.println("runQuery - end");  
   int i=0;  
   int i=0;  
  System.out.println(&quot;Iterating: &quot; + i);
   while (rs.next()) {  
   while (rs.next()) {  
     if (++i % 1000 == 0) {
     if (++i % 1000 == 0) System.out.println("Iterating: " + i);  
      System.out.println(&quot;Iterating: &quot; + i);  
     uid = rs.getString("UID");  
    }
     if (uid == null) uid = "";  
     uid = rs.getString(&quot;UID&quot;);  
     dpto = rs.getString("DPTO");  
     if (uid == null) uid = &quot;&quot;;  
     if (dpto == null) dpto = "";  
     dpto = rs.getString(&quot;DPTO&quot;);  
     if (dpto == null) dpto = &quot;&quot;;  
     updateOrCreateDocument(db,uid);  
     updateOrCreateDocument(db,uid);  
   }  
   }  
   stmt.close();  
   stmt.close();  
   System.out.println(&quot;Total: &quot; + i);  
   System.out.println("Total: " + i);  
  }
  }


8) Criação ou Atualização do documento no repositorio NSF
# Create or update documents in the NSF repository:
 
Com a chave primária, armazenada na string '''key''', verificamos a existencia ou nao do documento. Se o resultado for vazio, criamos um novo documento senao verificamos por mudancas e entao atualizamos o documento.


  private void updateOrCreateDocument(Database db, String key) throws Exception {  
  private void updateOrCreateDocument(Database db, String key) throws Exception {  
Line 524: Line 503:
   boolean saveDoc = false;  
   boolean saveDoc = false;  
   doc = view.getDocumentByKey(key, true);  
   doc = view.getDocumentByKey(key, true);  
   if ('''doc == null'''){  
   if (doc == null){  
     doc = db.createDocument();  
     doc = db.createDocument();  
     doc.replaceItemValue(&quot;Form&quot;,&quot;person&quot;);  
     doc.replaceItemValue("Form","person");  
     doc.replaceItemValue(&quot;uid&quot;, uid);  
     doc.replaceItemValue("uid", uid);  
     doc.replaceItemValue(&quot;dpto&quot;, dpto);  
     doc.replaceItemValue("dpto", dpto);  
     saveDoc = true;  
     saveDoc = true;  
   } else {  
   } else {  
  if (!uid.equals(&quot;&quot;) &amp;&amp; !uid.equals(doc.getItemValueString(&quot;uid&quot;))){  
    if (!uid.equals("") && !uid.equals(doc.getItemValueString("uid"))){  
       doc.replaceItemValue(&quot;uid&quot;, uid);  
       doc.replaceItemValue("uid", uid); saveDoc = true;  
      saveDoc = true;  
     }  
     }  
     if (!dpto.equals(&quot;&quot;) &amp;&amp; !dpto.equals(doc.getItemValueString(&quot;dpto&quot;))){  
     if (!dpto.equals("") && !dpto.equals(doc.getItemValueString("dpto"))){  
       doc.replaceItemValue(&quot;dpto&quot;, dpto);  
       doc.replaceItemValue("dpto", dpto); saveDoc = true;  
      saveDoc = true;  
     }  
     }  
   }  
   }  
   if (saveDoc) {
   if (saveDoc) doc.save(true, false, true);     
    doc.save(true, false, true);     
  }
   doc.recycle();  
   doc.recycle();  
   view.recycle();  
   view.recycle();  
  }
  }


9) No metodo principal '''NotesMain''', chamamos os demais metodos.
# In the main method '''NotesMain''', call the other methods:


  public void NotesMain() {  
  public void NotesMain() {  
Line 553: Line 528:
     Session session = getSession();  
     Session session = getSession();  
     AgentContext agentContext = session.getAgentContext();  
     AgentContext agentContext = session.getAgentContext();  
    // (Your code goes here)
     Database notedDb = agentContext.getCurrentDatabase();  
     Database notedDb = agentContext.getCurrentDatabase();  
     System.out.println(&quot;ImportPersonInfo - start&quot;);  
     System.out.println("ImportPersonInfo - start");  
     '''initDB'''();  
     initDB();  
     '''runMain'''(notedDb);  
     runMain(notedDb);  
     System.out.println(&quot;ImportPersonInfo - end&quot;);  
     System.out.println("ImportPersonInfo - end");  
   } catch(Exception e) {  
   } catch(Exception e) {  
     e.printStackTrace();  
     e.printStackTrace();  
Line 565: Line 538:
  }
  }


==2.3. Configurando o UserInfoConfig.xml para utilizar o repositorio local==
== 2.3. Configuring UserInfoConfig.xml to Use the Local Repository ==


Como um pre-requisito para a desta secao eh termos uma chave primaria entre o primeiro repositorio LDAP e o segundo repositorio NSF. Neste caso o valor do campo uid sera populado com os mesmos valores.
As a prerequisite, there must be a primary key shared between the LDAP repository and the NSF repository. In this case, the uid field will contain the same values.


1) Edite o arquivo '''UserInfoConfig.xml'''
# Edit the '''UserInfoConfig.xml''' file
# Locate the '''&lt;/Storage&gt;''' tag and add the new repository:


2) Localize a tag '''&lt;/Storage&gt;''', e adicione o novo repositorio:
  &lt;Storage type="NOTES_CUSTOM_DB"&gt;  
 
  &lt;StorageDetails DbName="secbookstore.nsf" View="vwIndex"/&gt;  
  &lt;Storage type=&quot;NOTES_CUSTOM_DB&quot;&gt;  
  &lt;StorageDetails DbName=&quot;secbookstore.nsf&quot; View=&quot;vwIndex&quot;/&gt;  
  &lt;Details&gt;  
  &lt;Details&gt;  
  &lt;Detail Id=&quot;Dpto&quot; FieldName=&quot;Dpto&quot; Type=&quot;text/plain&quot;/&gt;  
  &lt;Detail Id="Dpto" FieldName="Dpto" Type="text/plain"/&gt;  
  &lt;Detail Id=&quot;PhotoURL&quot; FieldName=&quot;PhotoURL&quot; Type=&quot;text/plain&quot;/&gt;  
  &lt;Detail Id="PhotoURL" FieldName="PhotoURL" Type="text/plain"/&gt;  
  &lt;Detail Id=&quot;Photo&quot; FieldName=&quot;photo&quot; Type=&quot;image/jpeg&quot; /&gt;  
  &lt;Detail Id="Photo" FieldName="photo" Type="image/jpeg"/&gt;  
  &lt;/Details&gt;  
  &lt;/Details&gt;  
  &lt;/Storage&gt;
  &lt;/Storage&gt;


3) Localize a seção &lt;'''ParamsSets&gt;''' e adicione os novos campos
# Locate the &lt;'''ParamsSets&gt;''' section and add the new fields:


  &lt;ParamsSets&gt;  
  &lt;ParamsSets&gt;  
   &lt;Set SetId=&quot;0&quot; params=&quot;MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,'''Dpto'''&quot;/&gt;  
   &lt;Set SetId="0" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,'''Dpto'''"/&gt;  
   &lt;Set SetId=&quot;1&quot; params=&quot;MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,'''Dpto'''&quot;/&gt;  
   &lt;Set SetId="1" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,'''Dpto'''"/&gt;  
  &lt;/ParamsSets&gt;  
  &lt;/ParamsSets&gt;
 
4) Localize a seção &lt;'''BlackBoxConfiguration&gt;''' e adicione os novos blackbox
 
&lt;BlackBox type=&quot;NOTES_CUSTOM_DB&quot; name=&quot;com.ibm.sametime.userinfo.userinfobb.UserInfoNotesCustomBB&quot; MaxInstances=&quot;4&quot;/&gt;
 
5) Salve e Feche o arquivo.
 
6) Reinicie o servidor Sametime Community
 
=3. Apresentar a foto do usuário armazenada no Perfil do IBM Connections=


Por fim iremos utilizar as fotos armazenadas nos Perfis do IBM Connections para serem apresentadas nos IBM Sametime.
# Locate the &lt;'''BlackBoxConfiguration&gt;''' section and add the new blackbox:


No IBM Connections, as fotos do Perfis ficam armazenados no banco de dados, dentro da tabela EMPINST.PHOTO. O acesso direto às tabelas do Perfis não é suportado pela IBM, mas iremos utilizar aqui neste artigo.
&lt;BlackBox type="NOTES_CUSTOM_DB" name="com.ibm.sametime.userinfo.userinfobb.UserInfoNotesCustomBB" MaxInstances="4"/&gt;


Aqui vamos adicionar adicionar 2 campos princípais:
# Save and close the file
# Restart the Sametime Community server


* Photo: Campo RichText utilizado para armazenar o binario da foto utilizada pelo Sametime Connect Client</li>
= 3. Displaying the User Photo Stored in IBM Connections Profile =
* PhotoURL: Url da foto utilizado pelo Sametime Web Client (Sametime Proxy)


Para evitar a comparação de 2 arquivos, criamos o campo '''updated''', a qual possui a data de alteração do arquivo de foto.
Finally, we will use photos stored in IBM Connections Profiles to display them in IBM Sametime.


==3.1. Importando as fotos do IBM Connections==
In IBM Connections, profile photos are stored in the database, in the table EMPINST.PHOTO. Direct access to profile tables is not officially supported by IBM, but we will use it in this article.


O nosso agente está divido em 6 partes:
We will add 2 main fields:


1) Definição das variáveis
* Photo: A RichText field used to store the binary photo used by the Sametime Connect Client
* PhotoURL: URL of the photo used by the Sametime Web Client (Sametime Proxy)


2) Inicialização do Driver JDBC do DB2
To avoid comparing two files, we create the '''updated''' field, which stores the date when the photo was last updated.


3) Execucao da query
== 3.1. Importing Photos from IBM Connections ==


4) Criar o arquivo de Imagem
Our agent is divided into 6 parts:


5) Criação ou Atualização do documento no repositório NSF
# Defining variables
# Initializing the DB2 JDBC Driver
# Executing the query
# Creating the image file
# Creating or updating the document in the NSF repository
# Main method


6) Método principal
Since it follows the same approach as the previous section, we will only show the differences.


Por se tratar da mesma abordagem usada na seção anterior, vamos apresentar apenas as diferenças
=== Procedure ===


'''Procedimento''':
# Create a new agent via '''Create''' -&gt; '''Design''' -&gt; '''Agent'''
 
1) Crie o novo através do menu '''Create''' -&gt; '''Design''' -&gt; '''Agent'''


{| class="wikitable"
{| class="wikitable"
|-
|-
! Campo !! Valor
! Field !! Value
|-
|-
| Name || ImportaFotosConnections
| Name || ImportPhotosConnections
|-
|-
| Alias || ImportaFotosConnections
| Alias || ImportPhotosConnections
|-
|-
| Comment ||
| Comment ||
Line 644: Line 610:
| Type || Java
| Type || Java
|-
|-
| Application || Secondary Book Store:\\<Nome do Servidor do Sametime>
| Application || Secondary Book Store:\\&lt;Sametime Server Name&gt;
|}
|}


Clique em '''OK'''
Click '''OK'''


'''Repita os passos 2, 3, 4 do item 2.2.'''
'''Repeat steps 2, 3, 4 from section 2.2.'''


5) Dentro da Classe '''JavaAgent''', vamos adicionar novas contantes de conexao ao DB2, variaveis globais a classe, ...
# In the '''JavaAgent''' class, add new DB2 connection constants and global variables:


  private static final String PHOTOS_TEMP_DIRECTORY = &quot;c:\\temp\\fotos\\img_&quot;;  
  private static final String PHOTOS_TEMP_DIRECTORY = "c:\\temp\\photos\\img_";  
  private static final String PHOTO_URL_PREFIX = &quot;<nowiki>http://connections.empresax.com.br/profiles/photo.do?uid=&quot;</nowiki>;  
  private static final String PHOTO_URL_PREFIX = "<nowiki>http://connections.companyx.com.br/profiles/photo.do?uid="</nowiki>;  
  private static final String SQL =  
  private static final String SQL =  
         &quot;SELECT E.PROF_UID UID, P.PROF_UPDATED UPDATED, P.PROF_IMAGE IMAGE &quot; +  
         "SELECT E.PROF_UID UID, P.PROF_UPDATED UPDATED, P.PROF_IMAGE IMAGE " +  
         &quot;FROM EMPINST.PHOTO P, &quot; +  
         "FROM EMPINST.PHOTO P, " +  
         &quot;EMPINST.EMPLOYEE E &quot; +  
         "EMPINST.EMPLOYEE E " +  
         &quot;WHERE P.PROF_KEY=E.PROF_KEY&quot;;  
         "WHERE P.PROF_KEY=E.PROF_KEY";  
   
   
  String updated = &quot;&quot;;  
  String updated = "";  
  byte[] photo_bytes ;  
  byte[] photo_bytes;  
 
A variável PHOTOS_TEMP_DIRECTORY armazena o diretorio temporario para armazenar as fotos.


A variável PHOTO_URL_PREFIX armazena a url para busca das fotos no IBM Connections.
PHOTOS_TEMP_DIRECTORY stores the temporary directory for the photos. 
PHOTO_URL_PREFIX stores the URL to retrieve photos from IBM Connections.


'''Repita os passos 6 do item 2.2.'''
'''Repeat step 6 from section 2.2.'''


7) Execucao da query
# Execute the query


Para cada linha retornada pelo SELECT, eh necessario criar um arquivo com a foto do usuario em um diretorio temporario, atraves do metodo '''createImageFile''', logo depois devemos chamar o metodo '''updateOrCreateDocument'''.
For each row returned by the SELECT, create a user photo file in the temporary directory using the '''createImageFile''' method, then call '''updateOrCreateDocument'''.


  private String createImageFile(String uid) throws Exception {  
  private String createImageFile(String uid) throws Exception {  
   String filename = PHOTOS_TEMP_DIRECTORY + uid + ".jpg";  
   String filename = PHOTOS_TEMP_DIRECTORY + uid + &quot;.jpg&quot;;  
   File outFile = new File(filename);  
   File outFile = new File(filename);  
   FileOutputStream fos = new FileOutputStream(outFile);  
   FileOutputStream fos = new FileOutputStream(outFile);  
Line 686: Line 650:
  private void runMain(Database db) throws Exception {  
  private void runMain(Database db) throws Exception {  
   Statement stmt = con.createStatement();  
   Statement stmt = con.createStatement();  
   System.out.println(&quot;runQuery - start&quot;);  
   System.out.println("runQuery - start");  
   ResultSet rs = stmt.executeQuery(SQL);  
   ResultSet rs = stmt.executeQuery(SQL);  
   System.out.println(&quot;runQuery - end&quot;);  
   System.out.println("runQuery - end");  
   int i=0;  
   int i=0;  
   System.out.println(&quot;Iterating: &quot; + i);  
   System.out.println("Iterating: " + i);  
   while (rs.next()) {  
   while (rs.next()) {  
     if (++i % 1000 == 0) {
     if (++i % 1000 == 0) System.out.println("Iterating: " + i);  
      System.out.println(&quot;Iterating: &quot; + i);  
     uid = rs.getString("UID");  
    }
     if (uid == null) uid = "";  
     uid = rs.getString(&quot;UID&quot;);  
     updated = rs.getString("UPDATED");  
     if (uid == null) uid = &quot;&quot;;  
     photo_bytes = rs.getBytes("IMAGE");  
     updated = rs.getString(&quot;UPDATED&quot;);  
     photo_bytes = rs.getBytes(&quot;IMAGE&quot;);  
     String filename = createImageFile(uid);  
     String filename = createImageFile(uid);  
     updateOrCreateDocument(db,uid,'''filename''');
     updateOrCreateDocument(db, uid, filename);
   }  
   }  
   stmt.close();  
   stmt.close();  
   System.out.println(&quot;Total: &quot; + i);  
   System.out.println("Total: " + i);  
  }
  }


# Creating or updating the document in the NSF repository


8) Criacao ou Atualizacao do documento no repositorio NSF
Using the primary key stored in the string '''key''', check whether the document exists. If it does not exist, create a new document; otherwise, check for changes and update it.
 
Com a chave primaria, armazenada na string '''key''', verificamos a existencia ou nao do documento. Se o resultado for vazio, criamos um novo documento senao verificamos por mudancas e entao atualizamos o documento.


  private void updateOrCreateDocument(Database db, String key) throws Exception {  
  private void updateOrCreateDocument(Database db, String key) throws Exception {  
   View view = db.getView(UID_LOOKUP_VIEW_NAME);  
   View view = db.getView(UID_LOOKUP_VIEW_NAME);  
   Document doc = null;  
   Document doc = null;  
   boolean saveDoc = false;  
   boolean saveDoc = false;  
   doc = view.getDocumentByKey(key, true);  
   doc = view.getDocumentByKey(key, true);  
   if (doc == null){  
   if ('''doc == null'''){  
     doc = db.createDocument();  
     doc = db.createDocument();  
     doc.replaceItemValue(&quot;Form&quot;,&quot;person&quot;);  
     doc.replaceItemValue("Form","person");  
     doc.replaceItemValue(&quot;uid&quot;, uid);  
     doc.replaceItemValue("uid", uid);  
     doc.replaceItemValue(&quot;updated&quot;, updated);  
     doc.replaceItemValue("updated", updated);  
     doc.replaceItemValue(&quot;PhotoURL&quot;, PHOTO_URL_PREFIX + uid);  
     doc.replaceItemValue("PhotoURL", PHOTO_URL_PREFIX + uid);  
     photo = doc.createRichTextItem(&quot;photo&quot;);  
     photo = doc.createRichTextItem("photo");  
     photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null);  
     photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null);  
     saveDoc = true;  
     saveDoc = true;  
   } else {  
   } else {  
     if (!uid.equals(&quot;&quot;) &amp;&amp; !uid.equals(doc.getItemValueString(&quot;uid&quot;))){  
     if (!uid.equals("") && !uid.equals(doc.getItemValueString("uid"))){  
       doc.replaceItemValue(&quot;uid&quot;, uid);  
       doc.replaceItemValue("uid", uid); saveDoc = true;  
      saveDoc = true;  
     }  
     }  
     ''' if (!updated.equals(&quot;&quot;) &amp;&amp; !updated.equals(doc.getItemValueString(&quot;updated&quot;))){ '''
     if (!updated.equals("") && !updated.equals(doc.getItemValueString("updated"))){  
       doc.replaceItemValue(&quot;updated&quot;, updated);  
       doc.replaceItemValue("updated", updated);  
       doc.removeItem(&quot;photo&quot;);  
       doc.removeItem("photo");  
       doc.replaceItemValue(&quot;PhotoURL&quot;, PHOTO_URL_PREFIX + uid);  
       doc.replaceItemValue("PhotoURL", PHOTO_URL_PREFIX + uid);  
       photo = doc.createRichTextItem(&quot;photo&quot;);  
       photo = doc.createRichTextItem("photo");  
       photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null);  
       photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null);  
    saveDoc = true;  
      saveDoc = true;  
     }  
     }  
   }  
   }  
   if (saveDoc) {
   if (saveDoc) doc.save(true, false, true);     
    doc.save(true, false, true);     
  }
   doc.recycle();  
   doc.recycle();  
   view.recycle();  
   view.recycle();  
}
}


9) No metodo principal '''NotesMain''', chamamos os demais metódos.
# In the main method '''NotesMain''', call the other methods.


==3.2. Configurando o UserInfoConfig.xml para retornar as fotos==
== 3.2. Configuring UserInfoConfig.xml to Return Photos ==


1) Edite o arquivo '''UserInfoConfig.xml'''
# Edit the '''UserInfoConfig.xml''' file
# Locate the '''&lt;Storage&gt;''' section and add the new fields:


2) Localize a secao '''&lt;Storage&gt;''', e adicione o novos campos:
  &lt;Storage type="NOTES_CUSTOM_DB"&gt;  
 
  &lt;StorageDetails DbName="secbookstore.nsf" View="vwIndex"/&gt;  
  &lt;Storage type=&quot;NOTES_CUSTOM_DB&quot;&gt;  
  &lt;StorageDetails DbName=&quot;secbookstore.nsf&quot; View=&quot;vwIndex&quot;/&gt;  
  &lt;Details&gt;  
  &lt;Details&gt;  
   &lt;Detail Id=&quot;Dpto&quot; FieldName=&quot;Dpto&quot; Type=&quot;text/plain&quot;/&gt;  
   &lt;Detail Id="Dpto" FieldName="Dpto" Type="text/plain"/&gt;  
   '''&lt;Detail Id=&quot;PhotoURL&quot; FieldName=&quot;PhotoURL&quot; Type=&quot;text/plain&quot;/&gt;'''
   &lt;Detail Id="PhotoURL" FieldName="PhotoURL" Type="text/plain"/&gt;  
   '''&lt;Detail Id=&quot;Photo&quot; FieldName=&quot;photo&quot; Type=&quot;image/jpeg&quot; /&gt;'''
   &lt;Detail Id="Photo" FieldName="photo" Type="image/jpeg"/&gt;  
  &lt;/Details&gt;  
&lt;/Details&gt;  
  &lt;/Storage&gt;
  &lt;/Storage&gt;


3) Salve e Feche o arquivo.
# Save and close the file
 
# Restart the Sametime Community server
4) Reinicie o servidor Sametime Community
 
= Conclusão =


Este artigo descreveu como extender o IBM Sametime para prover informações aos usuários armazenadas em diversos repositórios. Mostramos como criar um repositório próprio buscando informações de outras fontes como um base SQL. E importando as fotos dos perfis do IBM Connections.
= Conclusion =


Caso voce tenha um Cluster de IBM Sametime Community Server, basta criar uma replica da base secbookstore.nsf e copiar o arquivo UserInfoConfig.xml para os demais servidores.
This article described how to extend IBM Sametime to provide user information stored in various repositories. We showed how to create a custom repository by retrieving information from other sources, such as an SQL database, and importing photos from IBM Connections profiles.


Espero que o artigo seja útil no seu dia como Administrador Sametime.
If you have an IBM Sametime Community Server cluster, simply create a replica of the secbookstore.nsf database and copy the UserInfoConfig.xml file to the other servers.


= Recursos =
I hope this article is useful in your day-to-day work as a Sametime Administrator.


= Ver também =
= Ver também =

Latest revision as of 12:49, 15 August 2025

I migrated the article "Customizing the IBM Sametime Business Card" from IBM developerWorks, after the sale of Sametime to HCL.

Summary

Having access to information about coworkers in day-to-day work should be quick and easy. This article describes, in a practical way, how to make such information available through the IBM Sametime Connect Client and the IBM Sametime Web Client (Sametime Proxy Server).

Introduction

Our story begins with a multinational company that, a year ago, made IBM Sametime and IBM Connections available to its employees. Today, thanks to these two tools, employees communicate better and more effectively.

One morning, the company’s IT Director returns from a meeting with the Vice President of Marketing. The Marketing Department requested that new information be displayed in the IBM Sametime Business Card.

The IBM Sametime Connect Client and Web Client Business Card displays information about a contact, including Name, Title, Telephone, and even Photos. Users view this information when they are in a Chat window or when they hover the mouse pointer over a contact in the Contact List.

ADD SCREENSHOT WITH BUSINESS CARD

Figure 1: Business Card Screen

The IT Director challenges the IBM Sametime Administrator to meet the following requirements:

1. Add new fields from LDAP;

2. Add fields from the Company’s HR System;

3. Display the user’s photo stored in the IBM Connections Profile;

With these needs in hand, the IBM Sametime Administrator begins the customizations.

1. Adding New Fields from LDAP

By default, the configuration interface of the Sametime System Console Business Card allows you to select which information will be displayed in the Sametime client.

1.1. Changing Attributes through the Sametime System Console

The available attributes are:

  • Name
  • Company
  • E-mail
  • Telephone
  • Location
  • Title
  • Photo

Since the Business Card information is obtained from the LDAP Directory to which the IBM Sametime Community Server is connected, we must map the attributes:

Attribute Name LDAP Attribute
Name cn
Title title
Location postalAddress
Telephone telephoneNumber
E-mail Address mail
Photo jpegPhoto
Company ou

You can change the Business Card through the Sametime System Console.

Follow these steps:

1) Access the Sametime System Console

2) In the navigation panel (on the left), click on Sametime System Console

3) Expand Sametime Servers and click on Sametime Community Servers.

4) Click on the Deployment Plan of the Sametime Community Server.

5) Click on the Business Card tab.

6) Change the attributes, click OK to save the settings.

Restart the Sametime Community Server.

Figure 2: Business Card Configuration in the Sametime System Console

Despite the ease of using the Sametime Administration Console, we are limited to the available fields. Moreover, not all user information is stored in a single repository, such as an LDAP directory, due to performance reasons. For example, user photos.

1.2. The Business Card Subsystem

The Business Card subsystem of IBM Sametime retrieves information from the repositories configured in the UserInfoConfig.xml file.

These repositories can be:

  • An LDAP directory
  • A Notes application
  • A Java class, which in turn retrieves information from a SQL database or an ERP/SAP system.

The interface (API) that accesses these repositories is called the BlackBox.

The process of retrieving information by the Client from the repository is described below:

  1. The Sametime Client makes an HTTP (GET) request to a Sametime Community server.
  2. When the request reaches the Sametime/Domino HTTP server, Domino redirects the request to the UserInfoServlet servlet.
  3. The UserInfoServlet servlet reads the UserInfoConfig.xml file, which contains the connection information for the repositories.
  4. The servlet queries each repository to retrieve the information.
  5. The data from the repositories is returned to the UserInfo servlet.
  6. The servlet combines the responses from these repositories and converts them into an XML document.
  7. The HTTP server sends the XML document to the client, which extracts the data and displays it on the client.

Figure 3: Business Card Configuration in the Sametime System Console

1.3. Adding Attributes via UserInfoConfig.xml

The UserInfoConfig.xml file allows greater flexibility in customizing the data displayed on the Business Card.

The file is generated during the installation of the Sametime Community Server and is located in the following directory (depending on the platform):

MS Windows:

<DOMINO_INSTALL_DIR>\UserInfoConfig.xml  
Example: C:\IBM\Domino\UserInfoConfig.xml

Linux/AIX:

<DOMINO_DATA>\UserInfoConfig.xml  
Example: /local/notesdata

When opening the UserInfoConfig.xml file, you will basically see two sections:

In the <Storage type="LDAP"> section, we have the connection information to the LDAP server:

<Storage type="LDAP"> 
<StorageDetails
    HostName="ldapserver_hostname" Port="636" 
    UserName="" Password="" SslEnabled="false" SslPort="636" 
    BaseDN="" Scope="2" 
    SearchFilter="(&amp; (objectclass=organizationalPerson)(|(cn=%s)(givenname=%s)(sn=%s)(mail=%s)))" /> 
<!-- Add another StorageDetails tag to support another LDAP server. The listing order implies the searching order --> 
<!-- Scope: 0=OBJECT_SCOPE 1=ONELEVEL_SCOPE 2=SUBTREE_SCOPE--> 

Listing 1: Section with LDAP connection information

While in the Details section, we have the mapping between Sametime attributes and LDAP fields:

    <Details> 
      <Detail Id="MailAddress" FieldName="mail" Type="text/plain"/> 
      <Detail Id="Name" FieldName="cn" Type="text/plain"/> 
      <Detail Id="Title" FieldName="title" Type="text/plain"/> 
      <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/> 
      <Detail Id="Telephone" FieldName="telephoneNumber" Type="text/plain"/> 
      <Detail Id="Company" FieldName="ou" Type="text/plain"/> 
      <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg"/> 
    </Details> 
  </Storage> 
</Resources> 
<ParamsSets> 
  <Set SetId="0" params="MailAddress,Name,Title,Location,Telephone,Photo,Company"/> 
  <Set SetId="1" params="MailAddress,Name,Title,Location,Telephone,Photo,Company"/> 
</ParamsSets> 

Listing 2: Section with attribute mappings

We can now start the procedure to add new fields:

Procedure

Before starting, as a precaution, make a backup of the UserInfoConfig.xml file.

  1. Edit the UserInfoConfig.xml file.
  2. Locate the Details section.
  3. Locate the Telephone entry and add the mobile number:
<Detail Id="Telephone" FieldName="telephoneNumber,mobile" Type="text/plain" DisplaySeparator=" / " /> 
  1. Before the </Details> tag, add two new fields as follows:
<Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/>
<Detail Id="City" FieldName="city" Type="text/plain"/>
  1. Locate the ParamsSets section and add the new fields:
<ParamsSets> 
  <Set SetId="0" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State"/> 
  <Set SetId="1" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State"/> 
</ParamsSets> 
  1. Save and close the file.

The final file looks as follows:

  <Details> 
     <Detail Id="MailAddress" FieldName="mail" Type="text/plain"/> 
     <Detail Id="Name" FieldName="cn" Type="text/plain"/> 
     <Detail Id="Title" FieldName="title" Type="text/plain"/> 
     <Detail Id="Location" FieldName="postalAddress" Type="text/plain"/> 
     <Detail Id="Telephone" FieldName="telephoneNumber,mobile" Type="text/plain" DisplaySeparator=" / " /> 
     <Detail Id="Company" FieldName="ou" Type="text/plain"/> 
     <Detail Id="Photo" FieldName="jpegPhoto" Type="image/jpeg"/> 
     <Detail Id="State" FieldName="stateOrProvince" Type="text/plain"/> 
     <Detail Id="City" FieldName="city" Type="text/plain"/>
    </Details> 
  </Storage> 
</Resources> 
<ParamsSets> 
  <Set SetId="0" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State"/> 
  <Set SetId="1" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State"/> 
</ParamsSets> 

Listing 3: Result of modifications to UserInfoConfig.xml

  1. Restart the Sametime Community Server.

To test the changes in UserInfoConfig.xml, you can check the result returned by the UserInfoServlet.

In a browser, go to:

http://<Sametime_Server>/servlet/UserInfoServlet?operation=3&setid=2&userid=<Test_Account_Name>

where:

Parameter Description
operation Operation to be performed. Use the value 3.
setid Parameter set to be returned, defined in the ParamsSets section of UserInfoConfig.xml
userid Lookup key

Example:

 http://stserver/servlet/UserInfoServlet?operation=3&setid=2&userid=uid=ebasso

Where the result is:

<?xml version="1.0" encoding="UTF-8"?>
<userinfo>
  <user id ="uid=ebasso">
   <field name="MailAddress" type="" error="UNAVAILABLE"/>
   <field name="Name" type="text/plain">Enio Rubens Basso</field>
   <field name="Title" type="text/plain">IT Specialist</field>
   <field name="Location" type="" error="UNAVAILABLE"/>
   <field name="Telephone" type="text/plain">61-3333-4444 / 61-9999-9999</field>
   <field name="City" type="text/plain">Brasilia</field>
   <field name="State" type="text/plain">DF</field>
  </user>
</userinfo>

Listing 4: Servlet return

As you can see, if an attribute value in LDAP is not populated, UserInfoServlet returns error="UNAVAILABLE".

Adding Fields from the Company HR System

To address the second challenge from the Marketing department, the Sametime Administrator needs to retrieve information from the Company's HR System.

To avoid querying the HR System constantly, we will create a local repository to store the information to be displayed on the Sametime Business Card.

To perform this task, we need to complete 3 activities:

  • Create the local repository
  • Import data from the HR System
  • Configure UserInfoConfig.xml

As a prerequisite, you need to have Domino Designer installed on your computer and configured to access the Sametime Community server. Use the Sametime Administrator account to avoid restrictions when creating the database and running operations on the server.

2.1. Creating the Local Repository

We will create a Notes application (NSF) to store the data.

  1. Open Domino Designer
  2. Create a new database via the menu File -> Application -> New

Fill in the following fields:

Field Value
Server <Sametime Server Name>
Title Secondary Book Store
File name secbookstore.nsf
Server Local
Template Select -Blank-

Click OK

Figure 4: Dialog Box for creating a new application

Creating the Form

  1. Create a new form via Create -> Design -> Form
Field Value
Name person
Alias person
Comment
Application Secondary Book Store:\\<Sametime Server Name>

Click OK

  1. Create the key field named uid via Create -> Design -> Field

Enter uid for Name and set Type to Text.

Figure 5: Dialog box to create a new field

  1. Create the remaining fields used in this article:
Field Name Type Description
dpto Text Department
updated Text Photo update date
PhotoURL Text Image URL
Photo Rich Text Lit Image file

Save the form via File -> Save.

Figure 6: New fields created

Creating the Index View

  1. Create a new view via Create -> Design -> View

Fill in:

Field Value
View Name vwIndex
View Type Shared
Selection Formula By Formula
Select Conditions Select @All

Click Save and Customize

Figure 7: Dialog box to create a view

  1. Click the column #, select Field, and choose uid.

Figure 8: Setting the column value

  1. In the properties dialog, click the second tab and under Sort, select Ascending.

Figure 9: Setting the sort order

Save the view via File -> Save.

Now we have a local secondary repository.

2.2. Importing Data from the HR System

The Company HR System is stored in a DB2 database.

In this section, we will create a Java agent to retrieve information from the SQL database and populate the local NSF repository.

The agent is divided into 5 parts:

  1. Defining variables
  2. Initializing the DB2 JDBC Driver
  3. Executing the query
  4. Creating or updating documents in the NSF repository
  5. Main method

Preparation

To access DB2, we must add the DB2 JDBC (type 4) drivers.

Copy the following files into <DOMINO_INSTALL_DIR>\ibm-jre\jre\lib\ext:

  • db2jcc.jar
  • db2jcc_license_cu.jar

Figure 10: DB2 JDBC driver configuration

Procedure

  1. Create a new agent via Create -> Design -> Agent
Field Value
Name ImportPersonInfo
Alias ImportPersonInfo
Comment
Type Java
Application Secondary Book Store:\\<Sametime Server Name>

Click OK

  1. In Basics, set On Schedule -> Daily, and Target to None.

  1. In Security, select 3. Allow restricted operations with full administration rights.

  1. Click on JavaAgent.java to create the code.

  1. In the JavaAgent class, define the DB2 connection constants and global variables:
// CONSTANTS
private static final String JDBC_CLASS = "com.ibm.db2.jcc.DB2Driver"; 
private static final String JDBC_URL = "jdbc:db2://db2srv.empresax.com.br:50000/RHS"; 
private static final String JDBC_USERID = "STUSER"; 
private static final String JDBC_PASSWORD = "STUSER"; 
private static final String UID_LOOKUP_VIEW_NAME = "vwIndex"; 
private static final String SQL = 
"SELECT UID, DPTO " + 
"FROM EMPLOYEE " + 
"ORDER BY UID"; 

// GLOBAL VARIABLES
Connection con; 
String uid = ""; 
String dpto = "";
  1. Initialize the DB2 JDBC Driver:
private void initDB() throws Exception { 
 Class.forName(JDBC_CLASS).newInstance(); 
 con = DriverManager.getConnection(JDBC_URL, JDBC_USERID, JDBC_PASSWORD); 
}
  1. Execute the query:
private void runMain(Database db) throws Exception { 
 Statement stmt = con.createStatement(); 
 System.out.println("runQuery - start"); 
 ResultSet rs = stmt.executeQuery(SQL); 
 System.out.println("runQuery - end"); 
 int i=0; 
 while (rs.next()) { 
   if (++i % 1000 == 0) System.out.println("Iterating: " + i); 
   uid = rs.getString("UID"); 
   if (uid == null) uid = ""; 
   dpto = rs.getString("DPTO"); 
   if (dpto == null) dpto = ""; 
   updateOrCreateDocument(db,uid); 
 } 
 stmt.close(); 
 System.out.println("Total: " + i); 
}
  1. Create or update documents in the NSF repository:
private void updateOrCreateDocument(Database db, String key) throws Exception { 
 View view = db.getView(UID_LOOKUP_VIEW_NAME); 
 Document doc = null; 
 boolean saveDoc = false; 
 doc = view.getDocumentByKey(key, true); 
 if (doc == null){ 
   doc = db.createDocument(); 
   doc.replaceItemValue("Form","person"); 
   doc.replaceItemValue("uid", uid); 
   doc.replaceItemValue("dpto", dpto); 
   saveDoc = true; 
 } else { 
   if (!uid.equals("") && !uid.equals(doc.getItemValueString("uid"))){ 
     doc.replaceItemValue("uid", uid); saveDoc = true; 
   } 
   if (!dpto.equals("") && !dpto.equals(doc.getItemValueString("dpto"))){ 
     doc.replaceItemValue("dpto", dpto); saveDoc = true; 
   } 
 } 
 if (saveDoc) doc.save(true, false, true);    
 doc.recycle(); 
 view.recycle(); 
}
  1. In the main method NotesMain, call the other methods:
public void NotesMain() { 
 try { 
   Session session = getSession(); 
   AgentContext agentContext = session.getAgentContext(); 
   Database notedDb = agentContext.getCurrentDatabase(); 
   System.out.println("ImportPersonInfo - start"); 
   initDB(); 
   runMain(notedDb); 
   System.out.println("ImportPersonInfo - end"); 
 } catch(Exception e) { 
   e.printStackTrace(); 
 } 
}

2.3. Configuring UserInfoConfig.xml to Use the Local Repository

As a prerequisite, there must be a primary key shared between the LDAP repository and the NSF repository. In this case, the uid field will contain the same values.

  1. Edit the UserInfoConfig.xml file
  2. Locate the </Storage> tag and add the new repository:
<Storage type="NOTES_CUSTOM_DB"> 
<StorageDetails DbName="secbookstore.nsf" View="vwIndex"/> 
<Details> 
<Detail Id="Dpto" FieldName="Dpto" Type="text/plain"/> 
<Detail Id="PhotoURL" FieldName="PhotoURL" Type="text/plain"/> 
<Detail Id="Photo" FieldName="photo" Type="image/jpeg"/> 
</Details> 
</Storage>
  1. Locate the <ParamsSets> section and add the new fields:
<ParamsSets> 
  <Set SetId="0" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,Dpto"/> 
  <Set SetId="1" params="MailAddress,Name,Title,Location,Telephone,Photo,Company,City,State,Dpto"/> 
</ParamsSets>
  1. Locate the <BlackBoxConfiguration> section and add the new blackbox:
<BlackBox type="NOTES_CUSTOM_DB" name="com.ibm.sametime.userinfo.userinfobb.UserInfoNotesCustomBB" MaxInstances="4"/>
  1. Save and close the file
  2. Restart the Sametime Community server

3. Displaying the User Photo Stored in IBM Connections Profile

Finally, we will use photos stored in IBM Connections Profiles to display them in IBM Sametime.

In IBM Connections, profile photos are stored in the database, in the table EMPINST.PHOTO. Direct access to profile tables is not officially supported by IBM, but we will use it in this article.

We will add 2 main fields:

  • Photo: A RichText field used to store the binary photo used by the Sametime Connect Client
  • PhotoURL: URL of the photo used by the Sametime Web Client (Sametime Proxy)

To avoid comparing two files, we create the updated field, which stores the date when the photo was last updated.

3.1. Importing Photos from IBM Connections

Our agent is divided into 6 parts:

  1. Defining variables
  2. Initializing the DB2 JDBC Driver
  3. Executing the query
  4. Creating the image file
  5. Creating or updating the document in the NSF repository
  6. Main method

Since it follows the same approach as the previous section, we will only show the differences.

Procedure

  1. Create a new agent via Create -> Design -> Agent
Field Value
Name ImportPhotosConnections
Alias ImportPhotosConnections
Comment
Type Java
Application Secondary Book Store:\\<Sametime Server Name>

Click OK

Repeat steps 2, 3, 4 from section 2.2.

  1. In the JavaAgent class, add new DB2 connection constants and global variables:
private static final String PHOTOS_TEMP_DIRECTORY = "c:\\temp\\photos\\img_"; 
private static final String PHOTO_URL_PREFIX = "http://connections.companyx.com.br/profiles/photo.do?uid="; 
private static final String SQL = 
        "SELECT E.PROF_UID UID, P.PROF_UPDATED UPDATED, P.PROF_IMAGE IMAGE " + 
        "FROM EMPINST.PHOTO P, " + 
        "EMPINST.EMPLOYEE E " + 
        "WHERE P.PROF_KEY=E.PROF_KEY"; 

String updated = ""; 
byte[] photo_bytes; 

PHOTOS_TEMP_DIRECTORY stores the temporary directory for the photos. PHOTO_URL_PREFIX stores the URL to retrieve photos from IBM Connections.

Repeat step 6 from section 2.2.

  1. Execute the query

For each row returned by the SELECT, create a user photo file in the temporary directory using the createImageFile method, then call updateOrCreateDocument.

private String createImageFile(String uid) throws Exception { 
 String filename = PHOTOS_TEMP_DIRECTORY + uid + ".jpg"; 
 File outFile = new File(filename); 
 FileOutputStream fos = new FileOutputStream(outFile); 
 fos.write(photo_bytes); 
 fos.close(); 
 return filename; 
} 

private void runMain(Database db) throws Exception { 
 Statement stmt = con.createStatement(); 
 System.out.println("runQuery - start"); 
 ResultSet rs = stmt.executeQuery(SQL); 
 System.out.println("runQuery - end"); 
 int i=0; 
 System.out.println("Iterating: " + i); 
 while (rs.next()) { 
   if (++i % 1000 == 0) System.out.println("Iterating: " + i); 
   uid = rs.getString("UID"); 
   if (uid == null) uid = ""; 
   updated = rs.getString("UPDATED"); 
   photo_bytes = rs.getBytes("IMAGE"); 
   String filename = createImageFile(uid); 
   updateOrCreateDocument(db, uid, filename);
 } 
 stmt.close(); 
 System.out.println("Total: " + i); 
}
  1. Creating or updating the document in the NSF repository

Using the primary key stored in the string key, check whether the document exists. If it does not exist, create a new document; otherwise, check for changes and update it.

private void updateOrCreateDocument(Database db, String key) throws Exception { 
 View view = db.getView(UID_LOOKUP_VIEW_NAME); 
 Document doc = null; 
 boolean saveDoc = false; 
 doc = view.getDocumentByKey(key, true); 
 if (doc == null){ 
   doc = db.createDocument(); 
   doc.replaceItemValue("Form","person"); 
   doc.replaceItemValue("uid", uid); 
   doc.replaceItemValue("updated", updated); 
   doc.replaceItemValue("PhotoURL", PHOTO_URL_PREFIX + uid); 
   photo = doc.createRichTextItem("photo"); 
   photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null); 
   saveDoc = true; 
 } else { 
   if (!uid.equals("") && !uid.equals(doc.getItemValueString("uid"))){ 
     doc.replaceItemValue("uid", uid); saveDoc = true; 
   } 
   if (!updated.equals("") && !updated.equals(doc.getItemValueString("updated"))){ 
     doc.replaceItemValue("updated", updated); 
     doc.removeItem("photo"); 
     doc.replaceItemValue("PhotoURL", PHOTO_URL_PREFIX + uid); 
     photo = doc.createRichTextItem("photo"); 
     photo.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, filename, null); 
     saveDoc = true; 
   } 
 } 
 if (saveDoc) doc.save(true, false, true);    
 doc.recycle(); 
 view.recycle(); 
}
  1. In the main method NotesMain, call the other methods.

3.2. Configuring UserInfoConfig.xml to Return Photos

  1. Edit the UserInfoConfig.xml file
  2. Locate the <Storage> section and add the new fields:
<Storage type="NOTES_CUSTOM_DB"> 
<StorageDetails DbName="secbookstore.nsf" View="vwIndex"/> 
<Details> 
  <Detail Id="Dpto" FieldName="Dpto" Type="text/plain"/> 
  <Detail Id="PhotoURL" FieldName="PhotoURL" Type="text/plain"/> 
  <Detail Id="Photo" FieldName="photo" Type="image/jpeg"/> 
</Details> 
</Storage>
  1. Save and close the file
  2. Restart the Sametime Community server

Conclusion

This article described how to extend IBM Sametime to provide user information stored in various repositories. We showed how to create a custom repository by retrieving information from other sources, such as an SQL database, and importing photos from IBM Connections profiles.

If you have an IBM Sametime Community Server cluster, simply create a replica of the secbookstore.nsf database and copy the UserInfoConfig.xml file to the other servers.

I hope this article is useful in your day-to-day work as a Sametime Administrator.

Ver também