The Source for Java Technology Collaboration


Mobicents Google Talk Wake Up Service - XMPP and Google Portlet

The mobicents-examples sub-project includes XMPP Wake Up Service with Google Portlet as presentation layer.

Requirements

The Wake Up Service should receive requests from Google Portlet. Each request should contain millisecond difference between present time and time when request should be sent. Also it shoudl contain ,specific to client date when wake up call should be sent and valid user id (jid).

At millisecond difference basis service should start timer which fires when time has come to wake up user. Service should also give possibility to view requests for user which sends proper request.

Dependencies

  1. XMPP RA

Build, Deploy and Run

Quick start :
  1. Start the server.
  2. Check out the source code from the mobicents-examples project CVS - https://mobicents-examples.dev.java.net/source/browse/mobicents-examples/.
  3. Go to lib/xmppra and run ant deploy
  4. Change dir to WakeUpBot.
  5. Look for google-wakupbot.properties file, it resides in src/org/mobicents/examples/wakeupbot . Put valid Google Talk user and password ( only user id, without gmail.com or something like this)
  6. In WakeUpBotPage dir there is JNDI lookup ip in slee-conector.properties (located under src/java/org/mobicents/examples/wakeupbot/jsf/sleeconnector) It is automaticaly changed to jnpHost IP from lib/build.xml. Howver if You will deploy WakeUpBotPage? on different machine You will have to comment out suspicious copy task from build.xml file and put that IP in that file.Default value is 192.168.1.100.
  7. In WakeUpBot run ant deploy - this should deploy google bot and jsf app.
  8. Start GoogleTalk as different user than one in properties file, if he is not in contacts, add him.
  9. If all went well You should receive on presence message from bot.
  10. Change dir to WakeUpGoogleModule. Edit WakeUpModule.xml file and change IP to server which will run jsf app ( google portlets view )
  11. Copy this file to location wherefrom it can be accesed from outside.
  12. Go to www.google.com/ig and login there if You dont have account, create one.
  13. Use Add Content in left upper corner to add module. In the input field type fully quallified path to module XML file, in my case it was: http://www.iem.pw.edu.pl/~baranowb/WakeUpModule.xml.
  14. Use module You have added on www.google.com/ig to send request. Set date, type in the input field google talk user id ( in my case it was baranowb@gmail.com ) and sit back and wait for call from bot. ( You can use JSF app direct -- server/WakeUpBotPage/WakeUpRequests_1.faces )

Design

  • Two Service Building Blocks - WakeUpBotSbb? and WakeUpBotPacketSenderSbb?
  • One SLEE service - WakeUpBot?-service.xml
  • One Google Portlet moudule.
  • JSF App as Google Portlet view and bussines.

WakeUpBot?-service.xml

This file defines service. It defines id of service and it's root sbb, which will receive certain events fired to this service.

WakeUpBotSbb?

This will be the root of this service. This Building Block will be resposbile for storing and managing requests for one user, it has to grant that there wont be any collisions and requests will be "fired" in natural order.

Also it will receive messages with commands ( currently one ). Sbb should check those messages, build body for each response and forward this body to child Sbb. Each user that has requests or checks for current requests has different pair of WakeUpBotSbb? and WakeUpBotPacketSenderSbb?. This SBB has fiveCMP fields. One of them is ment to contain queues of requests, next two NullActivitiyContext? ( one for timer and second one for child sbb communication ). Last two CMP are suposed to contain reference to TimerFacility? and TimerID? of currently ticking timer.

WakeUpBotPacketSenderSbb?

This SBB is child sbb of WakeUpBotSbb. Its task is quite easy. It has to only send messages created by its "father" to Google Talk users. Frankly this code could be in WakeUpBotSbb? but it shows how to create child SBBs and fire events to them.

GooglePortletModule?

This simple file uses Google API to display JSF app as its content.

JSF App

This app is responsible for collecting relevant data and sending it to SLEE. Its design is quite simple. Main components are two custom tags, an input field and a button. Those tags enable user to "type" correct date in way that excludes wrong format of date, etc. Input field allows to type in Google Talk user id ( it should be valid, as for now App doesnt check it correctness). Bussines logic is beeing build by tags classes and two handler classes.

DateCollector class collects all needed data and uses SleeConnector class to send collected data to SLEE. DateColector uses custom jsf tags to retrieve relevant data from user. Each tag returns java.util.Date object. Those object contain different information ( day/mont/year and second/minute/hour - this way tags are much simpler ).

Source Code Review and Basic Concepts

WakeUpBot?-service.xml content

<service-xml>
    <service>
        <description/>
        <service-name>WakeUpBotService</service-name>
        <service-vendor>mobicents</service-vendor>
        <service-version>0.1</service-version>
        <root-sbb>
            <description/>
            <sbb-name>WakeUpBotSbb</sbb-name>
            <sbb-vendor>mobicents</sbb-vendor>
            <sbb-version>0.1</sbb-version>
        </root-sbb>
        <default-priority>0</default-priority>
    </service>
</service-xml>

Each Service is uniquely identified in the SLEE by its service-name, service-vendor and service-version. Services root SBB is identified in the same manner by its sbb-name,sbb-vendor and sbb-version.

WakeUpBot?-sbb-jar.xml

This file contains description of services SBBs. It describes each SBB and relationships beetwen them.

WakeUpBotSbb? xml description

<sbb>
      <sbb-name>WakeUpBotSbb</sbb-name>
      <sbb-vendor>mobicents</sbb-vendor>
      <sbb-version>0.1</sbb-version>
      <description>WakeUpBot for WakeUpService.</description>

      <sbb-ref>
         <description>This sbb is responsible for sending packets to google talk users</description>
         <sbb-name>WakeUpBotPacketSenderSbb</sbb-name>
         <sbb-vendor>mobicents</sbb-vendor>
         <sbb-version>0.1</sbb-version>
         <sbb-alias>PacketSenderSBB</sbb-alias>
      </sbb-ref>
As You can see sbb-name, sbb-vendor and sbb-versiona are identical with data specified in WakeUpBot?-service.xml. This way SLEE container knows that this SBB is root SBB. Second element in this snipper, the sbb-ref element creates a way to refer to other SBB in current SBB definition section of XML file. Again packet sender sbb is identified by three unique values and can be refered by its allias PacketSenderSBB.
<sbb-classes>
         <sbb-abstract-class>
            <sbb-abstract-class-name>org.mobicents.examples.wakeupbot.WakeUpBotSbb</sbb-abstract-class-name>
          <cmp-field>
      <cmp-field>
               <description>Contains HashMap of java.util.Date -> RequestHolder Object. Date indicates when wake up call should be made to user with RequestHolder.getUID()</description>
               <cmp-field-name>wakeUpQueue</cmp-field-name>
            </cmp-field>
            <cmp-field>
               <description>Holds ID of currenlty ticking timer.</description>
               <cmp-field-name>lastTimerID</cmp-field-name>
            </cmp-field>
            <cmp-field>
               <description>This is ActivityContext to which this sbb will attach itself upon creation and to which sbb will attach timer.</description>
               <cmp-field-name>timerNullActivityContext</cmp-field-name>
            </cmp-field>
            <cmp-field>
               <description>Stores TimerFacility reference so we wont have to do JNDI lookups</description>
               <cmp-field-name>timerFacility</cmp-field-name>
            </cmp-field>
            <cmp-field>
               <description>Stores NullActivity used to send event to packet sender sbb</description>
               <cmp-field-name>packetSenderNullActivityContext</cmp-field-name>
            </cmp-field>
           
This section describes abstract class of SBB. Each cmp-field element defines one CMP field. For each field SBBs abstract class has to have abstract setter and getter method.
For instance cmp-field with cmp-field-name timerFacility obligates us to create something liek that:
public abstract void setTimerFacility(TimerFacility tf);
public abstract TimerFacility getTimerFacility();
However return type and argument type may be different, according to ones need.

Additionaly in sbb-classes node one can define more specific informations abiut SBB. This is what I did.

         <get-child-relation-method>
               <sbb-alias-ref>PacketSenderSBB</sbb-alias-ref>
               <get-child-relation-method-name>getChildRelationPacketSender</get-child-relation-method-name>
               <default-priority>125</default-priority>
            </get-child-relation-method>
         </sbb-abstract-class>
         <sbb-local-interface>
            <sbb-local-interface-name>org.mobicents.examples.wakeupbot.WakeUpBotSbbLocalObject</sbb-local-interface-name>
         </sbb-local-interface>
         <sbb-activity-context-interface>
            <sbb-activity-context-interface-name>org.mobicents.examples.wakeupbot.WakeUpBotSbbActivityContextInterface</sbb-activity-context-interface-name>
         </sbb-activity-context-interface>

      </sbb-classes>
In snippet above there are three new definitions. First one defines relation beetwen two SBBs ( current SBB will have child/childs of type refered by sbb-alias-ref). Note the sbb-alias-ref element and compare it to with contents of sbb-ref element on top of this SBB definition.

First definition also defines name of abstract method we have to put in SBB abstract class. This method will return special Collection containing all child SBBs ( this collection has additional methods to manage creation and removal of childs ).

In case of this service and definitions in file child relation method should be implemented like this:

public abstract ChildRelation getChildRelationPacketSender();


Next part of definition file worth browsing through regards events:
<event event-direction="Receive" initial-event="True" mask-on-attach="False">
         <event-name>Presence</event-name>
         <event-type-ref>
            <event-type-name>org.jivesoftware.smack.packet.Presence</event-type-name>
            <event-type-vendor>org.jivesoftware.smack</event-type-vendor>
            <event-type-version>1.0</event-type-version>
         </event-type-ref>
         <initial-event-selector-method-name>initialEventCheckMethod</initial-event-selector-method-name>
      </event>

      <event event-direction="Receive" initial-event="True" mask-on-attach="False">
         <event-name>Request</event-name>
         <event-type-ref>
            <event-type-name>org.mobicents.examples.wakeupbot.events.WakeUpRequestEvent</event-type-name>
            <event-type-vendor>mobicents</event-type-vendor>
            <event-type-version>0.1</event-type-version>
         </event-type-ref>
         <initial-event-select variable="Address" />
      </event>
      <event event-direction="Fire" initial-event="False">
         <event-name>PacketRequest</event-name>
         <event-type-ref>
            <event-type-name>org.mobicents.examples.wakeupbot.events.PacketSendRequestEvent</event-type-name>
            <event-type-vendor>mobicents</event-type-vendor>
            <event-type-version>0.1</event-type-version>
         </event-type-ref>
      </event>
Last interesting part of sbb-jar.xml regarding WakeUpBotSbb are received/fired events definitions. Each event element defines one event which will be received, fired or both by SBB.
As You can see first two events "are initial". It means that when this event is delivered SLEE could have to create new SBBs to handle it.
In case of first event from this snippet behaviour of SLEE is determined by initial event selector method. This method tells SLEE how to compute convergence name. Convergence name is simply identifier of SBB/service. To shorten it if SBB with specific convergence name exists and there is event which should be delivered to SBB with that name, this event is delivered. However if SBB with specified convergence name doesnt exist SLEE tries to create it and deliver event to it.
Second event definition clearly gives us clue on what bassis convergence name will be created - by using address variable specified when this event was fired ( this part of code is in JSF app ).
Third one declares event that will be fired by root SBB.
Each event definition element has to have counterpart in SBBs methods. For instance for those three definition we have to create methods:
public void onPresence(org.jivesoftware.smack.packet.Presence packet,ActivityContextInterface aci) { ... }
public void onRequest(org.mobicents.examples.wakeupbot.events.WakeUpRequestEvent event,   ActivityContextInterface aci) { ... }
public abstract void firePacketRequest(PacketSendRequestEvent request,ActivityContextInterface aci, Address address);
!!!Note that fire event method is abstract, it's implemented by SLEE.

__WakeUpBotPacketSenderSbb__ definition isnt very interesting. It's second SBB definition in WakeUpBot?-ssb-jar.xml file. One thing in it is worth to mention. Identification fo this SBB has to be identical to sbb-ref declaration in root SBB:

<sbb>
      <sbb-name>WakeUpBotPacketSenderSbb</sbb-name>
      <sbb-vendor>mobicents</sbb-vendor>
      <sbb-version>0.1</sbb-version>
      <description>WakeUpBotpacketSender for WakeUpService.</description>
....
</sbb>

WakeUpBotSbb?_

Code has preaty good comments, however I will coemment here some parts of code and methods.

onRequest

__onRequest(org.mobicents.examples.wakeupbot.events.WakeUpRequestEvent event, ActivityContextInterface? aci)__ -- Simply creates new request for user which has been specified as recipent. If this is first request for user SLEE creates new pair od WakeUpBotSbb and WakeUpBotPacketSenderSbb.

If user has more than one request this function arranges requests in way which guarantee that each request will be sent in natural order ( for instance if new request should be sent earlier than old one, timer for old request is canceled and new timer is created for new request ). Its achieved like that:

// WE NEED A CLOCK TICKING FOR US
      if (localQueue.size() > 1) {

         if (localQueue.firstKey().equals(date)) {

            // TRUE?-IF WE GOT HERE IT MEANS THAT CURRENT REQUEST SHOULD BE
            // SENT AS FIRST
            // WE HAVE TO CANCEL CURRENTLY TICKING TIMER AND CREATE NEW ONE
            // FOR CURRENT REQUEST
            getTimerFacility().cancelTimer(getLastTimerID());
            setLastTimerID(null);
            Address address = new Address(AddressPlan.SMTP, event.getUID());
            attachTimer(date, address);
         }
         // FALSE?? WE DONT NEED ANY MAGIC WTH TIMERS, ONE THAT'S TICKING IS FINE
         return;
      }
      // QUEUE SHOULD HAVE EXACTLY 1 REQUEST, LETS CREATE TIMER FOR IT.

      Address address = new Address(AddressPlan.SMTP, event.getUID());
      attachTimer(date, address);
Important part of this snippet is creation of new address. Addresses are usd by SLEE in this service to route timer events to WakeUpBotSbb of proper user.

firePacketRequest

firePacketRequest(PacketSendRequestEvent? request, ActivityContextInterface? aci, Address address) ---This is SLEE implemented method. It is used to fire events from root SBB. Each event contains body of message that should be sent and UID of user to which it shoudl be delivered. Child SBB should be receiver of this event.

forwardRequest

forwardRequest(String body, String UID) --- uses firePacketRequest to dispatch request. Simple and conveniant method to encapsulate creation of event and fire procedure.

attachTimer

attachTimer(Date dateToFire, Address address) --- creates new timer which will fire one event to SBB identified by address. Timer is set to persistent and TimerEvent is allowed to be late not more than 5 seconds before is discarded. Stores new timer id in CMP field.

initialEventCheckMethod

initialEventCheckMethod(InitialEventSelector? ies) -- This method is very important. Its called every time event is delivered to SBB. Event is delivered first to this method. It examines it, retrieves UID of user, creates address object in way attachTimer did and "gives" SLEE hint that this address should be identifier of root SBB. If there is root SBB which is identified by specified address , event is delivered to it, if there is no such root SBB, SLEE creates one and delivers event to it. (events which could cause SLEE to create new SBB are called Initial event selectors. sbb.jar.xml defines which event are initial events).

This goal is achieved liek that:

// WE will receive either Message or Presence packetrs -> subclasses of
      // Packet
      logger.info("INITIAL EVENT SELECTOR METHOD CALLED!!! for: "
            + ((Packet) ies.getEvent()).getFrom());
      org.jivesoftware.smack.packet.Packet event = (Packet) ies.getEvent();

      // UID will propably look like xxxx@wp.pl/resourceString
      //we dont need resource string, lets loose it
      String UID = event.getFrom().split("/")[0];
      Address address = new Address(AddressPlan.SMTP, UID);
      //now use address as initial event selector
      ies.setAddress(address);
      ies.setAddressSelected(true);
      return ies;

TODO:

  1. Rewrite jsf app in eclipse. Currently its NetBeans? project. ( build-impl.xml file is hard to change, so all jars needed by jsf app are in lib dir - example is bigger because of this )

-- Main.baranowb - 21 Mar 2006

Topic MobicentsExampleGooglePortlet . { Edit | Ref-By | Printable | Diffs r19 < r18 < r17 < r16 < r15 | More }
 XML java.net RSS

Revision r19 - 30 Jan 2007 - 15:25:04 - Main.baranowb
Parents: MobicentsExamples