HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Intelligent Phones & Java Applications , by Robert Andreasen

The simple words, "Mr. Watson, come here. I want you," transformed communications in ways that Alexander Graham Bell probably never imagined. Although telecommunications has changed dramatically since that first call in 1876, there are still many aspects of our current phone system that would seem familiar to Bell.

Throughout its history the telephone has continued to be a relatively simple device connected by wire or fiber optic cable to an intelligent core. While the intelligence at the center has changed from operators at a switchboard to smart switches, the phone has remained a simple terminal at the periphery of the network. And, with this traditional host-terminal model, any change in phone services has to be programmed into the central switch.

Intelligent, Java-based IP phones, such as the xpressa phone offered by Pingtel Corporation (see Figure 1), are beginning to transform the historical host-terminal model. The debut of intelligent phones creates potentially revolutionary change because it's much easier to program changes at the periphery than at the core.

Figure 1
Figure  1:

With the old closed-system model it typically took two years and $2 million to develop, test, and deploy even a minor enhancement to the system. Furthermore, adding intelligence at the periphery offers enhanced scalability because processing tasks can be moved from the central server to the individual phones.

The Internet demonstrates how quickly change can occur when you eliminate central control over all innovation. The Internet provided a set of standards and a network for connecting any machine to any other machine, but the actual content and innovation often happened at the periphery.

Once developers recognized the potential of the Internet, the race was on. Suddenly anyone with a Web site could transform the network in new and unexpected ways. The development of portals, e-commerce sites, peer-to-peer file sharing, massive distributed computing solutions, messenger services, and streaming media all came from innovation at the edge. And there's no indication that the transformation has stopped or even slowed down.

Now, the same type of transformation is possible for telecommunications services. The combination of Java with an intelligent IP phone means the fun can finally begin in telecommunications as well. It opens up the phone system to a whole array of individualized services - things that you and your organization may want, even if there isn't a widespread audience for them.

By moving intelligence to the edge of the network, you can expect to see much greater innovation in phone services. End users will no longer be dependent upon their service providers for enhanced services, because they'll now be able to personalize their own phones in previously unimaginable ways.

This new phone architecture also leverages the same shareware phenomenon that has driven the development of thousands of new applications for the Palm PDA. When a product provides open APIs and developer kits, users benefit from the creativity of a wide community of developers. While this community often creates applications that meet their own specific needs and desires, many of these applications are eventually adopted by a wider group of users.

Java is central to this transformation because it's portable, flexible, widely adopted, and relatively easy to use. Thus, Java opens up the whole field of telecommunications to modification by a large community of developers. In addition, Java's ability to remotely load and execute apps allows new applications to be downloaded from the Web and installed by any user, anywhere in the world, without IT support.

This article covers the basics of Java programming for this new generation of intelligent IP phones. We discuss the technical background, the programming environment, and then generate a Java application for call filtering and routing - so you can see the code in action.

What's Possible
While we're discussing call filtering in this article, there are actually a wide variety of applications that can be developed for these new phones. With a Java phone you can create applications for everything from enhancing business productivity with simplified phone conferencing, speed dialing, enhanced caller ID, and transfer wizards to having fun with user-selected music from an MP-3 server and games such as head-to-head Tetris that can be played with other people. These applications represent only a small sample of what can be created for these new Java-enabled phones.

In addition, an IP phone's connection to the data network lets the phone become far more than just an instrument for transmitting voice. For example, you can integrate the phone with your PC so that you can dial from your Microsoft Outlook Contacts database, create and maintain phonebooks and a speed-dial directory, launch customer relationship management applications based on caller ID, and more.

Best of all, there are whole classes of applications that can be developed that haven't even been thought of yet. Remember, when you switch the intelligence to the periphery, the change comes rapidly and unpredictably. So the actual applications developed for the phone will be dependent upon what users want to accomplish.

Call Filter and Routing Application
Have you ever been bothered by telemarketers when you were trying to get something done? Ever wanted to accept certain phone calls while directing others to voice mail so you can deal with them later? Or transfer some calls to another person without picking up the phone? Or redirect calls to your cell phone so you get them as you travel about? The application we're about to describe and create allows you to do all this and more.

The call filtering application is a variation on the concept that many people use for automatically filtering and routing their e-mail. Much as an e-mail program looks at the sender's address or the subject line of an e-mail to determine what to do, an intelligent phone looks at the user ID, domain name, and a variety of other factors to determine what to do with an incoming call.

We chose call filtering as the subject of this article because it's easily understood by most people, and is a useful application that involves programming concepts that can be applied to a variety of other applications.

Programming Environment
While writing the new call filtering and routing application, we'll take advantage of a programming environment that includes an application framework for defining and installing applications, the Session Initiation Protocol (SIP), Simple Telephony API (STAPI), and programming hooks that control the phone's behavior.

The application framework for the Pingtel xpressa phone resembles the Java applet model used in Web browsers. Applications, or xpressions as they're called on Pingtel devices, must be packaged into a .jar file and contain both a class extending an application and a property file defining parameters and settings. Much like applets placed on a Web page, new xpressions are installed by reference and their URL is added to an application list on the phone.

The application class defines three methods in which developers can add logic - the onLoad, onUnload, and main methods.

public static void onLoad()

The onLoad method is invoked when the phone's class loader physically loads an xpression. Developers are expected to perform one-time initialization and add hooks and listeners. Depending on configuration settings, the xpression can be loaded at startup or on demand.

public static void onUnload()

The onUnload method is called immediately before the application framework unloads an xpression. Developers should remove hooks and listeners, and perform any cleanup that may be needed.

public void main(String argv[])

The main method is invoked when the xpression is executed. Applications can be configured to execute on startup or when selected from an application launcher.

Session Initiation Protocol
SIP is a very flexible HTTP-like protocol used by IP phones for call control. It defines how phones should communicate with one another to initiate calls, conference calls, transfer parties, drop calls, and perform other functions. Generally, developers don't need a working knowledge of SIP because they can use APIs such as JTAPI or STAPI to control a phone. However, as we build the call filtering and routing application, we'll utilize SIP addresses.

SIP addresses are similar to "mailto:" and "telnet:" addresses. These addresses identify a specific user at a given domain or device. Ignoring URL parameters, ports, and display names, an SIP address follows the form:

sip:[email protected]

Where "sip" represents the protocol, "userinfo" represents the user's name, and "host" represents the domain or device.

Simple Telephony API
STAPI, a simplified version of Java Telephony API (JTAPI), was designed by Pingtel to help developers easily create new Java applications for the IP phone. While developers can use JTAPI on an xpressa phone, JTAPI was originally designed for larger systems, such as call centers, and is fairly complex for a single end point. Therefore, STAPI is generally recommended for xpressa developers and is used throughout this article.

The call filtering and routing application performs its tasks before the phone rings and a call is actually completed. To accomplish this, the call filter hook works with a STAPI address that provides information about the caller and domain.

STAPI addresses can be expressed as either a PAddress or a PSIPAddress. A PAddress class defines an abstract address that represents addresses including traditional E164 phone numbers, SIP addresses, and addresses for other protocols. A PSIPAddress class extends a PAddress and adds methods for querying and changing the different parts of an SIP address.

Programming Hooks
The xpressa phone provides hooks that allow programmers to extend and alter the default behavior of the phone. The hook framework allows developers to supplement caller ID, alter the behavior of incoming calls, define dynamic dial plans, and provide advanced call filtering.

Hooks are callback routines that can be chained together sequentially. Whenever a decision needs to be made, such as how to filter a call or alert the user to an incoming call, a hook chain is walked. Each hook is executed in turn until a hook takes an action. Once a hook takes an action, there's no need to continue walking the hooks and the chain is considered terminated.

All hooks share a common interface and therefore look alike. A hook receives a hook-specific piece of data, checks the data's state, and then takes an action. In the call filter example the caller ID is the state information and the three possible actions are accepting, rejecting, or redirecting the incoming call.

public interface Hook {
public void hookAction(HookData data) ;
}

public class HookData {
public void terminate()
public Boolean isTerminated()
}

The Hook interface and HookData class are fairly simple. Whenever a hook takes an action, the terminate() method is invoked on the data object. Once terminated, the hook chain is no longer walked.

public class CallFilterHookData extends HookData {
public void accept()
public void reject()
public void redirect(PAddress address)
public PAddress getAddress()
}

The CallFilterHookData class provides three possible actions - accept, reject, and redirect - along with a method to get the incoming caller ID in the form of an address. In this case the hook will be walked sequentially to determine if it should accept, reject, or redirect the call. Once an action is taken, the hook is terminated.

Building the Framework
We begin by building a very flexible framework for the call filtering application. This framework allows us to define any number of conditions, actions, and rules that will determine the behavior of the phone when it receives an incoming call.

Figure 2 shows the general framework of a simple rules-based inference engine. When a rule is evaluated, it checks the condition that you specify and, if satisfied, applies the rule. The basic building blocks of the inference engine are shown below and in Listing 1; they demonstrate the structure of a condition, action, rule, condition set, and rule set.

Figure 2
Figure  2:

public interface ICondition {
public boolean isSatisfied(Object object);
}

The ICondition interface defines a condition that can either be satisfied or not. The condition is handed an application-specific object that affects the decision-making process. The condition statement returns true if satisfied and false if not.

public interface IAction {
public void apply(Object object);
}

The IAction interface defines an action that can be applied. Like the ICondition interface, an application-specific object is supplied to the implementer, which is expected to apply the action directly to the object parameter.

The Rule class (see Listing 1) combines IAction and ICondition interfaces into an entity that can be evaluated. The action and condition can be provided during construction or set later using the mutator method setRule. When the evaluate method is invoked, the action is applied if the condition has been satisfied.

While the diagram and code are relatively simple, the possibilities can be as simple or complex as you want because you can define any number of individual conditions, actions, and rules. In addition, you can create condition sets that contain one or more conditions and rule sets that contain one or more rules. You can also chain together these condition and filter sets so that multiple factors will be evaluated sequentially.

A ConditionSet class (see Listing 2) acts as a container for multiple conditions and also implements the ICondition interface. This composite design allows us to easily build complex conditions by ANDing them together. We could also add ORing abilities by introducing another composite container class. A RuleSet (see Listing 3) is a collection of rules that are processed sequentially until the first action is applied.

Building the Conditions
We'll now take the general framework and make it more specific by defining the conditions that we want to evaluate when someone calls us.

We'll begin by defining two universal conditions - AlwaysSatisfied and NeverSatisfied - that will make it easier to program certain features on the phone. We'll then add four specific conditions - UserMatches, DomainMatches, BeforeSpecifiedTime, and AfterSpecifiedTime - that will allow us to create a phone filter with wide applicability.

The initial six conditions listed in Table 1 have been selected to provide a usable filter and demonstrate the concepts. However, these are not the only conditions that could be established. In fact, you could define many other conditions, including any variable that can be measured and evaluated by the phone, such as whether you're currently on the phone.

Table 1

Because an IP phone is part of a network, you can also evaluate any information located on it. For example, this call filtering application can look for information in your Microsoft Outlook Contacts database or evaluate data from a corporate database.

The connection of the phone to the network opens up some interesting possibilities. For example, a condition could evaluate information in a customer service database and the call filter could direct incoming calls to specific people depending upon what products that customer owned. It could also prioritize calls from customers who had purchased a premier service contract or automatically escalate calls to an expert if the call came from someone who had already called with an urgent problem. All of the conditions controlling the phone's actions could be dynamically generated and evaluated as the database entries changed.

Let's begin by defining our two universals - the AlwaysSatisfiedCondition and the NeverSatisfiedCondition.

public class AlwaysSatisfiedCondition implements ICondition {
public boolean isSatisfied(Object object) {
return true ;
}
}

The universal AlwaysSatisfiedCondition is particularly useful for creating default rules. For example, Rule1 will fire if Condition1 is satisfied, Rule2 will fire if Condition2 is satisfied, and Rule3 will always fire if none of the previously defined rules are satisfied.

public class NeverSatisfiedCondition implements ICondition {
public boolean isSatisfied(Object object) {
return false ;
}
}

Although the NeverSatisfiedCondition is not used in this application, its concept and potential uses complement the AlwaysSatisfiedCondition.

The four other conditions specified in Table 1 are defined in the Java code shown in Listings 4-7. Particularly interesting is the code for the UserMatchesCondition and DomainMatchesCondition in Listings 4 and 5. These routines use the call filter hook infrastructure and SIP address information to determine the caller ID or domain address.

The UserMatchesCondition code (see Listing 4) brings together the discussion of SIP addresses and the call filter hook infrastructure to determine if a designated user matches the user who placed an incoming call.

To determine the caller ID, we safely cast the generic object parameter into the CallFilterHookData object, then pull out an abstract PAddress. While the Pingtel phone supports only SIP now, it could support other call-control protocols; this abstraction requires a cast from a PAddress to a PSIPAddress. We then compare the user field to determine if they match.

The DomainMatchesCondition (see Listing 5) is identical to the UserMatchesCondition except that we're checking the domain address instead of the user name.

The AfterSpecifiedTimeCondition (see Listing 6) is satisfied when the current time is greater than the pivot time supplied in the object's constructor. There's some code required to pull out the hour and minute components of the time/date, store them, and then compare them to the pivot time. The BeforeSpecifiedTimeCondition (see Listing 7) works in the same manner as the AfterSpecifiedTimeCondition, but is satisfied when the current time is less than the pivot time.

Building the Actions
The call filtering application will be able to perform the three possible actions defined in Table 2. For each incoming call the phone will accept the call, redirect it to another number, or reject it by transferring it directly to voice mail.

Table 2

The Java code in Listing 8 defines the three possible actions - accept, reject, and redirect the call. As part of the IAction interface the actions are given an object data parameter to work with. Since we specify this object parameter when evaluating the rule, it can be safely cast to a CallFilter- HookData object and manipulated as needed.

Figure 3 shows the original conceptual framework along with the conditions and actions that we've defined. You could easily add additional conditions or actions to this same framework.

Figure 3
Figure  3:

In Figure 3 note that the same framework can be applied to many different types of apps that could be built for a Java-enabled phone. Most phone applications will have a very similar structure consisting of conditions, actions, and rules. For example, we could easily transform this call filtering application into a distinctive ring application by simply changing the existing set of actions into ones that played different sound files depending on the caller or the time of day (see Figure 4).

Figure 4
Figure  4:

Building the Rules
We've now defined the conditions we want to evaluate and the potential actions that can be applied. However, we haven't tied together the conditions and actions. It's the function of the rules to combine these two classes of objects and then tell the program which action to take when a condition or condition set is met.

The rules that could be defined are almost infinite. We can set up the filter to compare phone or IP addresses, or to act differently depending upon the time of day, or if you're already on the phone. We can create rule sets that string rules together so that the execution is sequential and different actions are taken depending upon the results.

Let's look at a specific example of call filtering rules that a user might want to apply. In this example, it's two days before the end of the quarter and Martha Smith, the regional sales manager for Ultimate Widget Corporation, is in the midst of negotiating new contracts with her two biggest customers - Global, Inc., and Star Enterprises. She needs to spend most of her time on these deals in order to close them before the end of the quarter.

In the morning Martha decides to set up her call filter for the day. She wants to forward all her phone calls directly to voice mail except the calls from these two customers and from her husband Dan. She sets up the call filtering app to accept calls from Pete's office phone at Star Enterprise and from her husband Dan on any of his office, cell, or home phones. In addition, because she's negotiating with many decision-makers at Global - ranging from the department manager to the CFO and president - she wants to take any call that originates from the domain name global.com.

In the afternoon, Martha has to leave the office to visit another customer. Therefore, after 3:00 p.m. Martha wants all phone calls from Dan and Global to go to her cell phone and all calls from Star Enterprises to be transferred to her vice president of sales.

To make it easier to understand, let's create a table showing the rules we want to implement before we start programming. Table 3 shows four different types of conditions - a specified user on a particular phone, a specified user on many phones, any user from a specified domain, and all other users.

Table 3

We can now build the specific rules that will implement Martha's call filter. In this example we hard code the rules to demonstrate the Java programming behind the application. In actual use, the call filtering app would have a user interface that automatically writes these rules based upon user input. You can see the entire source code for the generic call filtering application, including the user interface, by visiting the JDJ Web site.

Each of our call filter rules relies on the CallFilterHook implementation given in Listing 9. This class defines our CallFilterHook implementation and performs two important functions for each call - it initializes the RuleSet described in this article and evaluates the RuleSet for each incoming call.

Initialization is performed in the constructor of the object. First, we create the two redirect addresses that are part of the After 3:00 p.m. rules. Please note the try/catch blocks around the creation of these addresses and recall that a PSIPAddress is really a URL. Next we define and add the Before 3:00 p.m. rules, After 3:00 p.m. rules, and finally the default/catch-all rule. With the exception of the default/catch-all rule, all the rules make use of helper classes defined later in this article.

The hook framework invokes the HookAction method when a new call is detected. Our implementation verifies that the HookData object parameter is really a CallFilterHookData object and evaluates the RuleSet configured in the CallFilterHook's constructor.

As you can see in Table 3, we need to define six rules to handle the calls from three different users during two different time periods. The Java classes that define these six call filtering rules are given in Listing 10. Each of these helper classes relies upon the CallFilterHook implementation given in Listing 9.

Table 4 summarizes the call filtering rules represented by the six Java classes in Listing 10. This table makes it easier to understand each of these classes because it shows the condition set that each rule will evaluate and the action it will take if the condition set is satisfied. It also lists the AlwaysSatisfied rule that will be invoked if none of the first six condition sets are satisfied.

Table 4

Installing the Hook
As the final step we need to install the CallFilterHook using the phone's hook manager. The CallFilterApp (see Listing 11) determines where the xpression will begin execution and provides a place to install the hook. Developers can define when their application will be loaded and executed through a properties file bundled in their .jar file (see Listing 12).

For this example, we'll load the application on phone startup and execute the application on demand. This means that installation will be performed by the onLoad method when the phone is powered up, and removal will be performed by the onUnload method when the phone is shut down.

Normally, the main() method would be invoked when the user selected the call filter's icon from the phone's application launcher. However, we haven't provided an implementation for the main() method here because it's part of the user interface that we haven't discussed. You can find the full source code, including the user interface, on the JDJ Web site.

Conclusion
We've demonstrated a simple Java application that can be used to filter and route calls on an intelligent Java-enabled IP phone. Most significantly, the application shows the ease with which new features can be added to a phone without relying upon a central programming environment to produce whatever apps people want. The two-year, $2 million wait for new phone functionality is a thing of the past with a Java-enabled phone.

We also showed a simple application of this filter to make Martha's life more focused and productive at a key juncture in the quarter. However, we've only hinted at the power of this phone. Remember that an IP phone is another device on a network, which means it can gather and evaluate information from anywhere on the network, be it LAN, WAN, or Web. For example, it's currently possible to set up your e-mail client or server to reject any e-mails that arrive from an address that's listed in one of the Internet databases of known spammers.

With the application we described here, you could set up your Java-enabled phone to eliminate all those telemarketing phone calls that interrupt your day at the most inconvenient times. Theoretically, an IP phone could refer to a similar Internet database that contains the phone numbers of all known telemarketers. Imagine, by referring to an up-to-the-minute database your phone could automatically generate a busy signal for any telemarketer trying to sell you a time-share condo or new programming tool.

If you really wanted to give some telemarketers a taste of their own medicine, you could change the actions to redirect each incoming telemarketing call to another telemarketer's phone number. You've now made your day more productive, and you have the satisfaction of knowing that these telemarketers will be spending their days trying to sell products to each other.

As you can see, an intelligent, Java-enabled IP phone opens up enormous possibilities for Java application developers. For the first time, many different types of applications can be easily developed and implemented for such a phone. Whole new classes of services can be easily built and deployed, creating a wealth of opportunities that were never imagined when Alexander Graham Bell first asked for help 125 years ago.

For More Information
The complete source code for the generic call filtering application is available at www.JavaDevelopersJournal.com. In addition, you can learn more about SIP, STAPI, and Java extensions for telephony and get more information about Java-based IP phones by visiting www.pingtel.com. A software developers kit for the Pingtel phone is available free at http://appdev.pingtel.com.

Author Bio
Robert Andreasen is the technical lead for Java applications at Pingtel. He has been writing applications for computers since 1993 and for phones since 2000, when the Pingtel xpressa was first available for independent developers. [email protected]

	



Listing 1:

A Rule class combines IAction and ICondition interfaces.

public class Rule {
    private IAction    m_action;
    private ICondition m_condition;
                
    public Rule(IAction action, ICondition condition) {
        setRule(action, condition);
    }
    
    public void setRule(IAction action, ICondition condition) {
        m_action = action;
        m_condition = condition;        
    }

    public boolean evaluate(Object data)  {
        boolean bDidApply = false;
            
        // Apply rule if condition is satisfied
        if (m_condition.isSatisfied(data)) {
            m_action.apply(data);
            bDidApply = true;
        }                
        return bDidApply;
    }
}

Listing 2:

A ConditionSet class.

public class ConditionSet implements ICondition {
    private Vector m_vConditions ;

    public ConditionSet() {
        m_vConditions = new Vector() ;
    }

    public synchronized void addCondition(ICondition condition) {
        m_vConditions.addElement(condition) ;
    }

    public synchronized void removeCondition(ICondition condition) {
        m_vConditions.removeElement(condition) ;
    }
    
    public synchronized boolean isSatisfied(Object data) {
        boolean bIsSatisifed = false ;
    
        // Loop through all of our conditions and look for the first one to fail
        for (int i=0; i<m_vConditions.size(); i++) {
            ICondition condition = (ICondition) m_vConditions.elementAt(i) ;
            bIsSatisifed = condition.isSatisfied(data) ;
            if (!bIsSatisifed)
                break ;                    
        }            
        return bIsSatisifed ;
    }
}

Listing 3:

A RuleSet class.

public class RuleSet extends Rule {    
    private Vector m_vRules ;
   
    public RuleSet() {
        m_vRules = new Vector() ;
    }

    public synchronized void addRule(Rule rule) {
        m_vRules.addElement(rule) ;
    }

    public synchronized void removeRule(Rule rule) {
        m_vRules.removeElement(rule) ;
    }

    public boolean evaluate(Object data) {
        boolean bDidApply = false ;
            
        // Loop through all of our rules and try to apply each one
        for (int i=0; i<m_vRules.size(); i++) {
            Rule rule = (Rule) m_vRules.elementAt(i) ;
            bDidApply = rule.evaluate(data) ;
            if (bDidApply)
                break ;
        }                
        return bDidApply ;
    }
}


/**
 * CONDITIONS
 */

Listing 4:

The UserMatchesCondition.

public class UserMatchesCondition implements ICondition {

    public UserMatchesCondition(String strName) {
        m_strName = strName ;
    }
               
    public boolean isSatisfied(Object data) {
        boolean bIsSatisifed = false ;
        
        if ((data != null) && (data instanceof CallFilterHookData)) {
            CallFilterHookData hookData = (CallFilterHookData) data ;
            
            PAddress address = hookData.getAddress() ;
            
            // Check to see if this is a SIP address. If this is a SIP address 
            // then pull the address apart and compare the appropriate pieces.           
            if (address instanceof PSIPAddress) {
                PSIPAddress sipAddress = (PSIPAddress) address ;                    
                if (m_strName.equalsIgnoreCase(sipAddress.getUser())) {
                    bIsSatisifed = true ;
                }
            } 
        }
        return bIsSatisifed ;
    }                
}

Listing 5:

The DomainMatchesCondition.

public class DomainMatchesCondition implements ICondition {
    private String m_strDomain ;  // The domain name that we will test against
        
    public DomainMatchesCondition(String strDomain) {
        m_strDomain = strDomain ;
    }
               
    public boolean isSatisfied(Object data) {
        boolean bIsSatisifed = false ;
        
        if ((data != null) && (data instanceof CallFilterHookData)) {
            CallFilterHookData hookData = (CallFilterHookData) data ;
                                   
            // Check to see if this is a SIP address. If this is a SIP address 
            // then pull the address apart and compare the appropriate pieces.           
            PAddress address = hookData.getAddress() ;
            if (address instanceof PSIPAddress) {
                PSIPAddress sipAddress = (PSIPAddress) address ;
                    
                if (m_strDomain.equalsIgnoreCase(sipAddress.getAddr())) {
                    bIsSatisifed = true ;
                }
            } 
        }            
        return bIsSatisifed ;
    }                
}

Listing 6:

The AfterSpecifiedTimeCondition.

public class AfterSpecifiedTimeCondition implements ICondition
{
    private int m_iPivotHour ;
    private int m_iPivotMinute ;
    
    public AfterSpecifiedTimeCondition(Date time) {            
        // Pull the hour and minute of the time parameter
        Calendar calendar = Calendar.getInstance() ;
        calendar.setTime(time) ;            
        m_iPivotHour = calendar.get(Calendar.HOUR_OF_DAY) ;
        m_iPivotMinute = calendar.get(Calendar.MINUTE) ;        
    }
               
    public boolean isSatisfied(Object object) {
        boolean bIsSatisfied = false ;
        
        // Validate parameter
        if ((object != null) && (object instanceof CallFilterHookData)) {
            PAddress address = ((CallFilterHookData) object).getAddress() ;
                                
            // Get the current time
            Calendar calendar = Calendar.getInstance() ;            
            int iCurrentHour = calendar.get(Calendar.HOUR_OF_DAY) ;
            int iCurrentMinute = calendar.get(Calendar.MINUTE) ;
                
            // Compare the current time against out stored pivot time
            if (iCurrentHour > m_iPivotHour)
                bIsSatisfied = true ;                
            else if ((iCurrentHour == m_iPivotHour) &&
                    (iCurrentMinute > m_iPivotMinute))
                bIsSatisfied = true ;
        }                        
        return bIsSatisfied ;            
    }
}

Listing 7:

The BeforeSpecifiedTimeCondition.

public class BeforeSpecifiedTimeCondition implements ICondition{
    private int m_iPivotHour ;
    private int m_iPivotMinute ;

    public BeforeSpecifiedTimeCondition(Date time) {            
        // Pull the hour and minute of the time parameter
        Calendar calendar = Calendar.getInstance() ;
        calendar.setTime(time) ;            
        m_iPivotHour = calendar.get(Calendar.HOUR_OF_DAY) ;
        m_iPivotMinute = calendar.get(Calendar.MINUTE) ;
    }
       
    public boolean isSatisfied(Object object) {
        boolean bIsSatisfied = false ;
        
        // Validate parameter
        if ((object != null) && (object instanceof CallFilterHookData)) {
            PAddress address = ((CallFilterHookData) object).getAddress() ;
                                
            // Get the current time
            Calendar calendar = Calendar.getInstance() ;            
            int iCurrentHour = calendar.get(Calendar.HOUR_OF_DAY) ;
            int iCurrentMinute = calendar.get(Calendar.MINUTE) ;
                
            // Compare the current time against out stored pivot time
            if (iCurrentHour < m_iPivotHour)
                bIsSatisfied = true ;                
            else if ((iCurrentHour == m_iPivotHour) &&
                    (iCurrentMinute < m_iPivotMinute))
                bIsSatisfied = true ;
        }                        
        return bIsSatisfied ;            
    }
}

/**
 * ACTIONS
 */ 

Listing 8:

The three possible call filtering actions: accept the call, reject the call, or redirect the call.

public class AcceptCallAction implements IAction{
    public void apply(Object data) {
        if ((data != null) && (data instanceof CallFilterHookData)) {
            CallFilterHookData hookData = (CallFilterHookData) data ;
            hookData.accept() ;
        }
    }                        
}    

public class RejectCallAction implements IAction {
    public void apply(Object data) {
        if ((data != null) && (data instanceof CallFilterHookData)) {
            CallFilterHookData hookData = (CallFilterHookData) data ;
            hookData.decline() ;
        }
    }                        
}    

public class RedirectCallAction implements IAction {
    private PAddress m_address ;    // address to redirecting to
    
    public RedirectCallAction(PAddress address) { 
        m_address = address ;
    }
        
    
    public void apply(Object data) {
        if ((data != null) && (data instanceof CallFilterHookData)) {
            CallFilterHookData hookData = (CallFilterHookData) data ;
            hookData.redirect(m_address) ;
        }
    }
}


/*
 * RULES CODE
 */

Listing 9:

The CallFilterHook implementation class.

public class CallFilterHook implements Hook {    
    RuleSet m_ruleSet = new RuleSet() ;
    
        
    public CallFilterHook() {
        PAddress cellphone = null ;
        PAddress vpSales = null ;
        
        try {
            vpSales = new PSIPAddress("[email protected]") ;
            cellPhone = new PSIPAddress("[email protected]") ;            
        } catch (MalformedURLException exception) { }               
                                
        // Before 3pm Rules
        m_ruleSet.addRule(new AcceptUserAtDomainBeforeTimeRule("pete", 
"star.com", new Date("3:00pm"))) ;
        m_ruleSet.addRule(new AcceptUserBeforeTimeRule("dan.smith", 
new Date("3:00pm"))) ;
        m_ruleSet.addRule(new AcceptDomainBeforeTimeRule("global.com", 
new Date("3:00pm"))) ;
            
        // After 3pm Rules        
        m_ruleSet.addRule(new RedirectUserAtDomainAfterTimeRule(vpSales, 
"pete", "star.com", new Date("3:00pm"))) ;
        m_ruleSet.addRule(new RedirectUserAfterTimeRule(cellphone, 
"dan.smith", new Date("3:00pm"))) ;        
        m_ruleSet.addRule(new RedirectDomainAfterTimeRule(cellphone, 
"global.com", new Date("3:00pm"))) ;
        
        // Default Rule
        m_ruleSet.addRule(new Rule(new RejectCallAction(), 
new AlwaysSatisfiedCondition())) ;                                                   
    }
        

    public void hookAction(HookData data) {        
        if (data instanceof CallFilterHookData) {
            CallFilterHookData filterData = (CallFilterHookData) data ;            
            m_ruleSet.evaluate(filterData) ;                                                
        }
    }
}

Listing 10:

The six call filtering rules.

public class AcceptUserAtDomainBeforeTimeRule extends Rule {
    public AcceptUserAtDomainBeforeTimeRule(String strUser, 
	String strDomain, Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new UserMatchesCondition(strUser)) ;
        conditions.addCondition(new DomainMatchesCondition(strDomain)) ;
        conditions.addCondition(new BeforeSpecifiedTimeCondition(time)) ;
        
        setRule(new AcceptCallAction(), conditions) ;
    }
}


public class AcceptUserBeforeTimeRule extends Rule {
    public AcceptUserBeforeTimeRule(String strUser, Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new UserMatchesCondition(strUser));
        conditions.addCondition(new BeforeSpecifiedTimeCondition(time)) ;
        
        setRule(new AcceptCallAction(), conditions) ;
    }
}


public class AcceptDomainBeforeTimeRule extends Rule{
    public AcceptDomainBeforeTimeRule(String strDomain, Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new DomainMatchesCondition(strDomain));
        conditions.addCondition(new BeforeSpecifiedTimeCondition(time)) ;
        
        setRule(new AcceptCallAction(), conditions) ;
    }
}


public class RedirectDomainAfterTimeRule extends Rule {    
    public RedirectDomainAfterTimeRule(PAddress address, 
	String strDomain, Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new DomainMatchesCondition(strDomain));
        conditions.addCondition(new AfterSpecifiedTimeCondition(time)) ;
        
        setRule(new RedirectCallAction(address), conditions) ;
    }
}


public class RedirectUserAfterTimeRule extends Rule implements Serializable {
    public RedirectUserAfterTimeRule(PAddress address, String strUser, 
	Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new UserMatchesCondition(strUser));
        conditions.addCondition(new AfterSpecifiedTimeCondition(time)) ;
        
        setRule(new RedirectCallAction(address), conditions) ;
    }
}


public class RedirectUserAtDomainAfterTimeRule extends Rule {
    public RedirectUserAtDomainAfterTimeRule(PAddress address, 
	String strUser, String strDomain, Date time) {
        ConditionSet conditions = new ConditionSet() ;
        
        conditions.addCondition(new UserMatchesCondition(strUser));
        conditions.addCondition(new UserMatchesCondition(strDomain));
        conditions.addCondition(new AfterSpecifiedTimeCondition(time)) ;
        
        setRule(new RedirectCallAction(address), conditions) ;
    }
}

Listing 11:

The CallFilterApp

public class CallFilterApp extends Application { 
    private static CallFilterHook s_hookCallFilter = null ;
    
    public static void onLoad() {
        // Get a reference to the hook manager
        HookManager hookManager = Shell.getHookManager() ;
        
// Install the CallFilterHook
       s_hookCallFilter = new CallFilterHook() ;
       hookManager.installHook(HookManager.HOOK_CALL_FILTER, s_hookCallFilter) ;
    }

    public static void onUnload() {
        // Get a reference to the hook manager
HookManager hookManager = Shell.getHookManager() ;

// Uninstall our CallFilterHook
       hookManager.deinstallHook(HookManager.HOOK_CALL_FILTER, s_hookCallFilter) ;
    }        
    
    public void main(String argv[])
    {
	// Insert GUI code here
    }    
}

Listing 12:

The properties file.

Title=Call Filter
Version=1.0
Hint=This applications allows you to filter your incoming calls
LongDescription = The application allows you to filter your incoming calls
Author=Robert Andreasen and William Saunders
LauncherIcon=callfilter.gif
MainClassName=com.pingtel.xpressa.apps.callfilter.CallFilterApp
InstallationMethod=URL
Load=SystemStart
Run=OnDemand
Type=Normal





  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.