Developer: Java
   DOWNLOAD
 Oracle JDeveloper 11g Technical Preview (includes ADF Faces Rich Client)
 Project Files
   TAGS
ria, java, All

Build a Google Talk Client Using Oracle ADF Faces Rich Client and the Active Data Service

Learn how to build a rich Java client that fully integrates with Google Talk.

By Lucas Jellema

Published March 2008

As Oracle OpenWorld 2007 made very clear, the integration of Web 2.0 concepts and trends into enterprise applications is a major trend in application development. Oracle Fusion Applications offer a good example of how combining very appealing, visually rich user interfaces with popular internet trends such as social bookmarking and instant messaging (IM) lead to attractive and productive applications. Communication integrated into the application’s user interface is an important element in the collaboration that this new style of application promotes. E-mail, Voice over IP, and Short Message Service are among the communication channels you might consider, but IM (chat) could well be the most powerful one.

This tutorial will demonstrate the development of a rich Web application that fully integrates with Google Talk, one of the major IM services. The open source Smack project provides a library of Java APIs to hook into the Extensible Messaging and Presence Protocol (XMPP), an IM communication protocol. Using Smack, you can very quickly create a Java application that speaks (and listens to) Google Talk—or any other XMPP-compliant IM service. Next, you will use Oracle ADF Faces Rich Client (part of Oracle JDeveloper 11g Technical Preview as of this writing) to create the user interface and, in particular, the Active Data Service that allows instant Web client updates (server-to-browser push) when messages are received. Finally, the Oracle ADF Faces Rich Client drag-and-drop facilities are used to quickly drop data records into chat messages.

Complete project files and sample code for every part of the tutorial are available here.

Getting Ready

  • Download and install Oracle JDeveloper 11g TP from OTN. Go to oracle.com/technology/products/jdev/11/index.html, download the 500Mb ZIP file, extract on your machine, and you are ready to go.
  • Get yourself a Google Talk account. Go to https://www.google.com/accounts/NewAccount?service=talk and create your free Google Talk account. Note: You can also add Google Talk as an additional component to an existing Google account.
  • Download the Google Talk client. Although not strictly necessary for this tutorial—after all we will be developing our own Google Talk client—it is probably useful to also download the Google Talk client from http://www.google.com/talk/. That way you can have the two clients talking to each other.
  • Download the Smack Java library for the XMPP protocol. Go to http://www.igniterealtime.org/downloads/index.jsp and look for the Smack library. Choose the ZIP (2.4Mb) or tar.gz (1.6Mb) file and download to your machine. Extract the archive. The files we are interested in for now are smack.jar and smackx.jar.

Part 1: Creating a Java Application That Speaks Google Talk

How to Set Up Smack and Connect to Google Talk for Programmatic Chats (Reading and Sending Messages)

Start Oracle JDeveloper 11g. Create a new application—call it, for example, GoogleTalk. Also create a new project; for example, GoogleTalkJavaClient.

Choose No Template [All Technologies] from the Application Template menu. Open the Project Properties dialog box from the right mouse button menu on the Project node or from the Tools menu. Go to the Libraries and Classpath node.

Add the smack.jar and the smackx.jar file archives to the project: click the Add JAR/Directory button. Browse to the Smack libraries and file, select both smack.jar and smackx.jar and click the Select button.

The JAR file is added to the project.

Click the OK button.

We will now create a class that will be able to send IM messages via Google Talk. Create a MessageSender class in package otn.adf.googletalk. Tell Oracle JDeveloper to add a main method.

package otn.adf.googletalk;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import  org.jivesoftware.smack.packet.Message;
public class  MessageSender {
    private static String username =  "YOUR_GOOGLE_TALK_USERNAME"; 
    private static String password =  "YOUR_GOOGLE_TALK_PASSWORD"; 
    ConnectionConfiguration connConfig;
    XMPPConnection connection;
                        
    public MessageSender() throws  XMPPException {
        connConfig = new  ConnectionConfiguration("talk.google.com", 5222,  "gmail.com");
        connection = new XMPPConnection(connConfig);
        connection.connect();
        connection.login(username, password);
    }
    
    public void sendMessage(String to, String  message ) {
        Message msg = new Message(to,  Message.Type.chat);
        msg.setBody(message);
       connection.sendPacket(msg);
    }
    
    public void disconnect() {
        connection.disconnect(); 
    }
    
    public static void main(String[] args)  throws XMPPException {
       MessageSender messageSender = new  MessageSender();
       messageSender.sendMessage("youraccount@gmail.com",  
             "Hello You. This is my first message sent programmatically using Smack API  and Google Talk.");
       messageSender.disconnect();
   }
}

Make sure you put in your own Google Talk account name and password on lines 10 and 11. Also put in your account—or your friend’s—on line 36, as the destination of this “Hello, world of instant messaging.”

The constructor of the MessageSender class creates a connection to the Google Talk server and logs in (line 17–21). When the MessageSender instance is created, the sendMessage method is invoked, with a destination (who to send the message to) and the message itself. Finally, the connection is closed in the disconnect method.

When you run the application, you will quickly receive your first Google Talk message sent by your own application through the Google Talk server:

Presence

One of the nice features of instant messaging is presence—the indication of which of our friends and contacts are available. Google supports presence and so does the Smack API.

We first can tell the world about our own presence, by executing these lines of code:

// tell the world (or at least our  friends) that we are around
Presence presence = new  Presence(Presence.Type.available);
presence.setMode(Presence.Mode.chat);
connection.sendPacket(presence);

Note: We can choose from several presence modes: available, chat, away, xa (extended away), and dnd (do not disturb).

Even more interestingly for us is that our program can learn the presence of all our contacts—called our “roster” in IM terms—or a specific contact. The list of all our contacts is retrieved like this:

Roster roster =  connection.getRoster();
Collection<RosterEntry> iter =  roster.getEntries();
for (RosterEntry entry : iter) {
     System.out.println(entry.getName()  + " (" + entry.getUser() + ")");
}

Receiving Messages

If all we could do was send messages, we would be in for a dull conversation. The next step we will make is adding listening capabilities to our “application.” We will create a MessageListener class that will look remarkably much like the MessageSender. In fact, let’s create it by first saving the MessageSender.java file as MessageListener.java. Then use search and replace to replace all occurrences of the word MessageSender with MessageListener.

Then have the class implement the PacketListener interface by adding implements PacketListener to the class specification. The only thing this interface requires us to do is implement the method processPacket, like this for example:

public void processPacket(Packet packet) {
    Message message = (Message)packet;
    System.out.println("Message  (from: "+message.getFrom()+"): "+message.getBody());
}

After we have registered our class as listener for messages with the connection, this method will be called whenever a message is sent by the Google Talk server to our connection. Registration of our class as a listener for chat messages to our account is done by adding these lines of code at the end of the constructor for MessageListener():

// to listen for incoming messages on  this connection 
PacketFilter filter = new  MessageTypeFilter(Message.Type.chat);
connection.addPacketListener((PacketListener)this,  filter);

Note: We need to add the following import statements to our class; however, Oracle JDeveloper will by and large do this for us as we add the code described above.

import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.Packet;

We are now ready to receive messages. If we change the main method of our MessageReceiver class as follows, we will receive our own message:

public static void main(String[] args) throws XMPPException, 
                                                     InterruptedException {
    MessageListener messageListener = new  MessageListener();
    messageListener.sendMessage("youraccount@gmail.com", 
                    "Hello You. This is my second message sent programmatically using Smack API and Google Talk.");
    // listen for 3 seconds
    Thread.sleep(3000);
    messageListener.disconnect();
}

Of course, instead of talking to yourself, it would be much better to listen to chat messages sent to you by your friends. If none are available at the present, Google Talk offers a series of “friends” that you can converse with. These so-called bots are programmatic Google Talk accounts that offer language translation. The bots have accounts like en2zh@bot.talk.google.com and nl2en@bot.talk.google.com, composed of the two-letter abbreviations for the source and target languages. (See http://googletalk.blogspot.com/2007/12/merry-christmas-god-jul-and.html for the announcement of the Google Talk translation bots).

We can simply send a chat message to a Google bot and it will send us a return message—great for quickly finding translations for simple words or phrases. We can change the main method yet again, to have it count in French from 1 to 10 (although, of course, you do not need a translation bot for doing so):

MessageListener messageListener = new  MessageListener();
    String[] englishCounting = new String  [] {"one", "two", "three", "four",  "five",
                                                    "six",  "seven", "eight", "nine", "ten"};
    for (int  i=0;i<englishCounting.length; i++) {
        messageListener.sendMessage("en2fr@bot.talk.google.com", englishCounting[i]);
        // add the slight pause in order to  increase the chances of 
        // receiving the replies in  the right order  
        Thread.sleep(500);
    }
    messageListener.disconnect();

The result of running the application this time is a count in French:

Note: The Google Talk bots run on many different threads on their server. That means that if there is only a small delay between the messages sent to the bot, the replies might return in a different order, as, for example, the thread handling five might actually respond sooner that the thread dealing with two. If we wait for 500ms or so before sending the next message, chances are that the replies will be received in the proper order.

Part 2: Build an Oracle ADF Faces Rich Client

Now that we have our basic Java application that can both send chat messages as well as receive them, it is time to find out how we can create a Web application that can do the same thing. We will use Oracle ADF Faces Rich for creating the user interface on top of the Oracle ADF data binding layer for connecting the Web tier to the data services that provide access to the content and the operations.

We will add a little extra functionality to the MessageSender: a property called roster, a collection of our “chat pals,” the friends we have added to Google Talk.

First, create a new class called TalkMate, a bean with two properties: toName and displayName.

package  otn.adf.googletalk;
public class  TalkMate {
     String toName;
     String displayName; 
     public TalkMate() {
     }
     public void setToName(String toName) {
         this.toName = toName;
     }
     public String getToName() {
         return toName;
     }
     public void setDisplayName(String  displayName) {
         this.displayName = displayName;
     }
     public String getDisplayName() {
         return displayName;
     }
}

Add the following private property to the MessageSender class:

private Collection<TalkMate> roster = new ArrayList();

and create accessor methods (getter and setter) for this property.

Then add the following lines of code to the MessageSender() constructor method:

Roster friendsRoster = connection.getRoster();
Collection<RosterEntry>  rosterIter = friendsRoster.getEntries();
for (RosterEntry entry : rosterIter) {
   TalkMate friend = new TalkMate();
   friend.setDisplayName(entry.getName()!=null?entry.getName():entry.getUser());
   friend.setToName(  entry.getUser());
   this.roster.add(friend);
} // rosterIter
// also add your own account to chat  with yourself for testing purposes 
TalkMate veryBestFriend = new TalkMate();
veryBestFriend.setDisplayName("Your  Own Display Name");
veryBestFriend.setToName(  "YOUR_OWN_GOOGLE_TALK_ACCOUNTNAME");
this.roster.add(veryBestFriend);

We will now create a MessageReceiver bean that exposes the chat listen capabilities in a friendly way to potential consumers. First create the following bean:

package  otn.adf.googletalk;
import  java.util.Date;
public class  ChatMessage {
     private Date timestamp;
     private String from;
     private String body;
     
     public ChatMessage() {
     }
     public ChatMessage(String from, String  body) {
          this.timestamp = new Date();
          this.from = from;
          this.body = body;
     }
     public void setTimestamp(Date timestamp) {
         this.timestamp = timestamp;
     }
     public Date getTimestamp() {
         return timestamp;
     }
     public void setFrom(String from) {
         this.from = from;
     }
     public String getFrom() {
         return from;
     }
     public void setBody(String body) {
         this.body = body;
     }
     public String getBody() {
         return body;
     }
}

Next create the MessageReceiver class, like this:

package otn.adf.googletalk;
import java.util.ArrayList;
import java.util.List;
public class  MessageReceiver {
    private List<ChatMessage> messages =  new ArrayList();
    public MessageReceiver() {
        try {
            MessageListener messageListener =  new MessageListener();
            messageListener.setReceiver(this);
        } catch (Exception e) {
        }
    }
    public void processMessage(String from,  String body) {
        ChatMessage message = new  ChatMessage(from, body);
        messages.add(0, message);
        for (ChatMessageListener  listener:messageListeners) {
            listener.processChatMessage(message);
        }
    }
    public void  setMessages(List<ChatMessage> messages) {
        this.messages = messages;
    }
    public List<ChatMessage>  getMessages() {
        return messages;
    }
}

We have to make a small change to the MessageListener class to make it all work together. Add a private property receiver to the MessageListener class, and create a getter and setter for it:

MessageReceiver receiver;

Next add the following line in method processPacket():

if (this.receiver != null) {
      receiver.processMessage(message.getFrom(), message.getBody());
}

This will make the MessageListener call processMessage on the MessageReceiver, which in turn will add a new ChatMessage to the messages collection in the MessageReceiver bean.

To expose the MessageSender and MessageReceiver beans in the Oracle ADF Data Control Palette, we have to publish both beans as data control. Let’s first focus on MessageSender: create and publish a data control from the right mouse button menu on the bean node:

After Oracle JDeveloper has finished creating the data control, we will find the MessageSender on the Data Control Palette, available for any Oracle ADF client to use:

Also publish a data control for the MessageReceiver in a similar way to the MessageSender, from the right mouse button menu.

Now create a new project in the Google Talk application; call it GoogleTalkWebClient. This project will contain our Web application, the JavaServer Faces (JSF) page, and associated configuration files.

After creating the Web project, select the GoogleTalkWebClient node in the navigator, go to the New Gallery and elect to create a JSF page.

Call the page, for example, ChatClient.jspx:

Click OK and the page gets created.

In this page, we would like to have two sections: one for writing and sending chat messages and one that lists the incoming messages. In this article, we will not bother too much with conducting chat conversations with multiple recipients—we will leave that as an exercise for the reader (don’t you just hate it when an author tries to get away with an expression like that).
Creating two sections in a page is handled very nicely by the Oracle ADF 11g Faces Rich Panel Splitter component. Drag this component from the Component Palette and drop it on the page. This will give us a panel with the two sections for sending and receiving messages. Set the width of the panel splitter to 100%.

Drag sendMessage() to the first facet. Drop it as an ADF Parameter Form.

Set meaningful prompts in the dialog box that follows:

The next dialog box is displayed and allows us to set default values for the input parameters for the sendMessage() operation. Click OK to accept the settings.

Oracle JDeveloper will create two inputText elements and a command button in the page, as well as the PageDefinition file with a variables iterator that contains two variables, two attributeValues elements, and an operationBinding element for the sendMessage() operation.

Change the dimensions of the messageBody element—set rows = 15 and columns = 60.

Delete the message_to inputText element. We will replace it with a list of all our contacts from which one contact can be selected.

Drag the displayName attribute in the roster collection on the MessageSender Data Control to our page and drop it just before the Message inputText, as an ADF Select One Choice.

The Edit List Binding dialog box appears. Select the variables iterator as the Base Data Source. Select MessageSender.root.roster as the List Data Source. Map Data Value sendMessage_to to List Attribute toName and pick displayName as the Display Attribute. Then click OK.

When you now run the ChatClient page, you can send chat messages to your Google Talk friends.

Our next step is to extend the ChatClient to also receive chat messages sent to us. Drag the Messages collection on the MessageReceiver Data Control to the second facet of the Panel Splitter on the ChatClient page. Drop this collection as an ADF Read-Only Table.

In the Edit Table Columns dialog box that appears, only retain the from and body columns.

Also drag the Execute operation on the Messages collection to the page and drop it as a command button underneath the Messages table we created above.

Change the text on the button from Execute to Refresh.

When you now run the page, you can use the left side of the page to send chat messages, while on the right side you can receive the replies from all your IM contacts, by clicking the Refresh button.

The architecture of this small application can be depicted like this:

The business service in our application is composed from the MessageSender and MessageReceiver classes that—aided by the Smack library—communicate with the Google Talk server. Both these classes have been published as ADF Data Controls. The JSF page has bound these data controls: an operation binding for the SendMessage operation on the MessageSender Data Control and a table binding for the Messages collection on the MessageReceiver Data Control. ADF Collections have a number of actions associated with them, one of which is Execute that will refresh the collection; this action is tied to the Refresh button.

Note that we can make the page operate a little more smoothly if we set partialSubmit to true on both buttons and add the Refresh button’s ID value to the partialTriggers attribute of the Messages table:

<af:commandButton actionListener="#{bindings.Execute.execute}"
                      text="Refresh"
                      disabled="#{!bindings.Execute.enabled}"
                       
                              
partialSubmit="true"  id="refreshReceived"/>
<af:table value="#{bindings.messages.collectionModel}" var="row"
             rows="#{bindings.messages.rangeSize}"
             emptyText="#{bindings.messages.viewable  ? 'No rows yet.' : 'Access Denied.'}"
             fetchSize="#{bindings.messages.rangeSize}"
   
                              
          partialTriggers="refreshReceived">
                            

I have sent one message to myself—and received a number from my oldest son trying to attract my attention:

Feel free to further decorate this page, it does the job right now but could do with a little brushing up. For example, set the attribute noWrap to false for the Body column that displays the chat message bodies; this will show the entire message body, in multiple lines of necessary. Using PanelBox containers is an easy way to adding some context frame and title to the various components in our page.

Part 3: Use the Oracle ADF Active Data Service Functionality to "Activate" the Web Client

OK, so now we have our Web client that speaks Google Talk. Sending messages, retrieving messages … what more could you possibly want? Well, one thing that is far from ideal is the fact that we have to click the Refresh button to see newly received messages. We would like our chat client to automatically report the messages it receives. And that is exactly the functionality we are about to add to the Web application, using the Oracle ADF Active Data Service (ADS).

The Oracle Fusion technology stack includes the ADS, which allows you to bind certain Oracle ADF Faces components to an active datasource using the Oracle ADF model layer or managed beans. Components including the table, the tree, and all types of graphs and charts can support the ADS—meaning that they update in near real time as the changes come in, in the underlying datasource.

To use the ADS, you need to have a data store that publishes events when data is changed, and you need to create business services that react to those events as well as the associated data controls to represent those services. An alternative way of leveraging the ADS capabilities of our components is by using a managed bean for the value attribute and having the bean implement the ActiveDataModel interface.

That is what we will be doing next: we will base the Messages table on a new managed bean that implements ActiveDataModel and registers itself with our MessageReceiver class for new ChatMessage events.

First, some changes to our business service.

Create interface ChatMessageListener:

package otn.adf.googletalk;
public interface ChatMessageListener {
   public void  processChatMessage(ChatMessage message);
}

Then apply some changes to the MessageReceiver class.

Insert these lines, which add a private list of ChatMessageListeners and a method that allows external parties to register listeners:

private  List<ChatMessageListener> messageListeners = new ArrayList();
public void  registerChatMessageListener(ChatMessageListener listener) {
    messageListeners.add(listener);
}

Add these lines to the processMessage method:

for (ChatMessageListener listener:messageListeners) {
    listener.processChatMessage(message);
}

This takes care of calling each registered listener whenever a new ChatMessage is received.

Now we create a new class in the GoogleTalkWebClient project: GoogleTalkActiveListener. This class implements the ChatMessageListener interface and registers with the MessageReceiver. It also extends the ADF CollectionModel, to make it act as the datasource for a table component. It needs to implement methods like getRowData(), getRowCount(), getRowIndex(), getRowKey(), setRowKey().

However, the really interesting part is that this class also implements the ActiveDataModel interface. This indicates to the table component it can register with this class as an ActiveDataListener for ActiveDataUpdateEvents. Whenever our GoogleTalkActiveListener class receives a new message, it will send a new ActiveDataUpdateEvent to all listeners; in our case only the table. The table uses server-to-client push to refresh the list of messages displayed in the browser.

The steps we have to go through are as follows:

  1. Create class GoogleTalkActiveListener.
  2. Some of the key sections in this class are listed below; the /Part3/ directory in the project files download contains a complete listing.

    In the constructor, we get hold of the MessageReceiver instance bound to the current page. We then register the new GoogleTalkActiveListener instance with the MessageReceiver as a ChatMessageListener.

    public GoogleTalkActiveListener() {
       DCBindingContainer bc = 
         (DCBindingContainer)FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet
                                                                  (FacesContext.getCurrentInstance(), 
                                                                   "#{bindings}", 
                                                                   Object.class);
          MessageReceiver messageReceiver = 
                  (MessageReceiver)bc.findDataControl("MessageReceiver").getDataProvider();
              messageReceiver.registerChatMessageListener(this);
      …
    

    The processChatMessage() is invoked by the MessageReceiver whenever a GoogleTalkChatMessage is received. In turn it creates an ActiveDataUpdateEvent with a single ActiveDateEntry for the new message. This event is sent to all registered ActiveDataListeners:

    public synchronized void  processChatMessage(ChatMessage message) {
        Map values = new HashMap();
        values.put("body", message.getBody());
        values.put("from",  message.getFrom());
        ActiveDataEntry newEntry = new  ChatActiveInsertDataEntry(values, "" + ++rowCount);
        List<ActiveDataEntry> updateList  = new ArrayList();
        updateList.add(newEntry);
        ActiveDataUpdateEvent event = 
                  new  ChatActiveDataUpdateEvent(this, rowCount, updateList);
        for (ActiveDataListener listener :  adsListeners) {
            try {
                  listener.dataChanged(event);
            } catch (Throwable e) {
            }
        } //for
    }
    
  3. Configure managed bean googleTalkActiveListener.
  4. <managed-bean>
        <managed-bean-name>googleTalkActiveListener</managed-bean-name>
        <managed-bean-class>googletalkwebclient.GoogleTalkActiveListener</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    
  5. Change the value attribute in the af:table component to have it refer to the managed bean instead of the table binding.
  6. <af:table  value="#{googleTalkActiveListener}" var="message">
    
  7. Remove the Refresh button from the ChatClient page. Not only is refreshing it the wrong thing (the table binding that the table does not use anymore), we also do not need a refresh option thanks to the ADS framework.

Here we see the ADS in action: I have clicked the chat message on the left side of the page. The message I have sent was received less than a second later by both my Google Talk Client and the ADS-enabled message table on the right side of the screen. Without any user interaction, the chat message appears in the browser, thanks to the server-to-client push.

Part 4: Drag and Drop to Drag Data Records into a Chat

In this last part, we will look at how the chat functionality we have created thus far can be integrated into a real, data-oriented page in our application. We will see how we can leverage the drag-and-drop functionality available in Oracle ADF 11g Faces Rich to allow users to drag records displayed in the page and drop them in the chat component, in order to add the gist of the record to the chat message that can be sent to a colleague or friend.

The steps for achieving this pretty advanced functionality are amazingly straightforward and require only a little coding in the third step:

  1. Provide data to display.
  2. Do the layout of the page, including the data table.
  3. Add the drag-and-drop functionality.

Provide Data to Display

First let’s provide some data for this application. To save you the trouble of getting access to a database, we use some beans to serve up some data. You will find the sources of these classes along with the resources for this tutorial.

The LibraryManager class extends ArrayList. It instantiates a number of Book Bean instances and makes them available to potential consumers. The LibraryManager class is configured as a managed bean:

<managed-bean>
    <managed-bean-name>libraryManager</managed-bean-name>
    <managed-bean-class>otn.adf.googletalk.model.LibraryManager</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Do the Layout of the Page, Including the Data Table

The ChatClient.jspx page is up for some redecorating. These are the steps:

  1. Add a panelSplitter component to the af:form node. Set the orientation of this panelSplitter to horizontal. Set its SplitterPosition to 400 and its Inline Style to width:100%; height:800px.
  2. Drag the existing panelSplitter to the first facet of the newly added panelSplitter. Set its orientation to vertical and its SplitterPosition to 320.
  3. If you have not already done so, set the noWrap attribute of the Body column in the Messages table to false, to allow for a multiline display of the received messages.
  4. Drag a panel box component from the Component Palette to the second facet in the new PanelSplitter. Set the text to Books.
  5. Drag a table component from the Component Palette to the second facet in the new PanelSplitter. Bind the table to the #{libraryManager} bean. Set the element type to class otn.adf.googletalk.model.Book; this will help Oracle JDeveloper populate the list of columns. Rearrange them in the order shown; remove the keywords column. Change the component type for the Thumbnail column to af:image. Click OK to have the table component added to the page.

    Next, set the Inline Style for the Thumbnail af:image component to width:60px.

 The structure window for the page should now look something like this:

At this point you can run the page and you would see a table with some book details to the right of the integrated chat facility on the left side of the page. However, the drag-and-drop integration is not yet available.

Add the Drag-and-Drop Functionality

To tell the Oracle ADF 11g Faces Rich infrastructure that records in a table are draggable (drag sources), we need to add a Collection Drag Source element to the table:

Specify the modelName attribute in this element:

<af:collectionDragSource actions="COPY"
                    modelName="libraryModel"/>

Likewise we have to specify what is to be the drop target for this drag-and-drop operation. Our drop target is the Message inputText element we use for typing in the chat message. We need to add a DropTarget element to this inputText:

We will specify the af:dataFlavor to accept instances of class org.apache.myfaces.trinidad.model.RowKeySet, which is what the data payload will be for drag-and-drop operations on records from a table; also set the discriminant attribute to libraryModel. Note: To make the inputText element refreshable through partial page rendering (PPR), we need to assign an ID to it—any unique ID value will do.

Because we need to do some manipulation of the message text, we will use a managed bean property to associate the value of the inputText with, instead of the variable from the pageDefinition; set the value attribute to #{googleTalkActiveListener.messageBody}. Finally, we need to specify how the drop event will be handled. For this, set the dropListener attribute on the dropTarget element to #{googleTalkActiveListener.handleDrop}.

<af:inputText id="msgbody"
       value="#{googleTalkActiveListener.messageBody}"
       label="Message"  columns="60"
       rows="10">
       <f:validator  binding="#{bindings.message.validator}"/>
       <af:dropTarget  actions="COPY"
                   dropListener="#{googleTalkActiveListener.handleDrop}">
       <af:dataFlavor  flavorClass="org.apache.myfaces.trinidad.model.RowKeySet" 
                                                        discriminant="libraryModel"/>
       </af:dropTarget>
</af:inputText>

We have now added two references to elements in the googleTalkActiveListener bean that do not yet exist. So the final step in getting the drag and drop to work takes place in the GoogleTalkActiveListener class. First, add properties messageBody and messageAddition (both of type String) and handlingDrop (Boolean). Generate accessors for property messageBody.

The handleDrop method is invoked whenever the msgBody element detects a drop event. It receives an instance of class DropEvent and returns a DnDAction instance. In our case, handling the drop event means finding out which book was dropped in the chat message field and adding a description of that book to the current chat message.

public DnDAction handleDrop(DropEvent  dropEvent) {
     Transferable dropTransferable =  dropEvent.getTransferable();
     DataFlavor<RowKeySet>  rowKeySetFlavor = 
     DataFlavor.getDataFlavor(RowKeySet.class);
     RowKeySet tableDrop =  dropTransferable.getData(rowKeySetFlavor);
     if (tableDrop != null) {
           // get the data for the  dropped rows
           CollectionModel dragModel = 
                  dropTransferable.getData(CollectionModel.class);
           StringBuilder rowOutput = new  StringBuilder();
           if (dragModel != null) {
               for (Object currKey :  tableDrop) {
                    dragModel.setRowKey(currKey);
                    Object rowValue = dragModel.getRowData();
                    rowOutput.append(rowValue);
               }
           }      
           messageAddition=  rowOutput.toString();
           handlingDrop=true;
           return DnDAction.COPY;
      } else {
           return DnDAction.NONE;
      }
}

From the drop event, we retrieve the Transferable object. We construct a DataFlavor instance for the class we know to expect in this particular drop event. From the Transferable object, we retrieve the RowKeySet with rowkeys for the Book records in the table collection that were dropped in this event. In a for loop we then extract each of the rowValues—Book instances—and append them (the result of their toString() method) to the StringBuilder. In this case, we have implemented the toString() method in the Book class like this:

@Override
public String toString() {
    return this.getTitle()+" by  "+this.getAuthor()+", published in "+this.getYear()+" by  "+this.getPublisher();
}

The result from all dropped records is then stored in the messageAddition property, and the handlingDrop property is set to true.

In the setMessageBody() method, which is invoked during the applyRequestValues phase, after the handleDrop method is done, we combine the current value of the message body sent from the client with the addition extracted from the dropped records:

public void setMessageBody(String  messageBody) {
      if (handlingDrop) {
           handlingDrop = false;>
           this.messageBody= messageBody  +" "+ messageAddition;
      } else {
           this.messageBody = messageBody;
      }
}

When you now run the ChatClient page, you drag any of the book records and drop it onto the Message field (and nowhere else).

When you drop the record, the book’s details are added to the message, as if you had typed them yourself:

Of course, after dropping one or more books, you can send the chat message; again just like you had typed in the book details yourself.

Congratulations, you have just built an "active" rich client app!

Resources

Introduction to Google Talk and the XMPP protocol, in two parts: http://www.adarshr.com/papers/xmpp and http://www.adarshr.com/papers/xmpp2.

Instant messaging in Java made easy: The Smack API— http://today.java.net/pub/a/today/2006/10/05/instant-messaging-for-jabber-with-smack.html.

About the Oracle ADF Active Data Service: see Appendix H in the Fusion Developer’s Guide for Oracle Application Development Framework. More about drag and drop can be found in the Oracle Fusion Middleware Web User Interface Developer’s Guide for Oracle Application Development Framework (Chapter 29, “Adding Drag and Drop Functionality”). These resources can all be downloaded from Oracle Technology Network. Go to: oracle.com/technology/products/jdev/11/index.html.

Another great source of information on how to use Oracle ADF Rich Client Components is the Oracle ADF Faces Rich Client Components demo, which can be downloaded from oracle.com/technology/products/adf/adffaces/11/doc/demo/adf_faces_rc_demo.html.

The AMIS Technology Weblog – http://technology.amis.nl/blog - frequently publishes articles by Lucas and his colleagues, on various Oracle tools and technologies, including Oracle JDeveloper and Oracle ADF.

Other blogs worth checking out with regard to Oracle ADF Rich Client Components include Andrejus Baranovskis’ http://andrejusb-samples.blogspot.com/, Frank Nimphius’ http://frank.thepeninsulasedge.com/, and Edwin Biemond’s http://biemond.blogspot.com/.


Lucas Jellema is Technology Manager with AMIS in Nieuwegein, The Netherlands, and is an Oracle ACE Director (Oracle Fusion Middleware). Lucas is a frequent blogger, author, and presenter at international conferences and workshops. He earns most of his money doing teaching and consulting around the Oracle SOA Suite and ADF technology.