A channel is the basic functional unit in IntelliPortal. Channels segment IntelliPortal functionality into discrete components like an investment channel or a statement viewer. Virtually all client application software is comprised of channels. They provide an easy, efficient mechanism for distributing both applications and data to the client workstation. One channel can call another channel and they can work together.
A channel is basically a directory associated with a channel name. This directory is populated with files that perform some function. This can be as simple as a collection of files to be displayed on the screen (e.g. a series of images or HTML pages) or it can be a complete Java application. This section explains how to create and organize these files to create a channel that runs in the IntelliPortal environment. See the Java source code for the Sample Channels on the IntelliPortal CD-ROM for examples of code that you can cut and paste, and for insights into how channels are created.
___________________________________
There are Java channels and HTML channels. Unless specifically noted, you can assume that the discussion refers to Java channels. Channels can also be visible or invisible. Invisible channels are usually helper, or administrative, channels that are started automatically or by other channels. Visible channels can appear to the user as separate applications or products, or what appears as an individual application might actually consist of several channels bundled together.
To develop a channel, you create the overall structure as described on the following pages. Then configure the channel with the IntelliPortal Server Administrator tool. This mostly involves telling the IntelliPortal server about the channel and identifying its links to any external data sources it might need. When published (using the Publisher tool), the channel files are copied to the transmitter and the channel properties file is created.
When a client subscribes to a channel the content is transmitted and installed. The subscription can be mandatory or discretionary, visible or invisible. Periodically, the channel is updated; new channel files are published to the transmitter, and only those files that have changed are transmitted to the client application. For example, new channel files might be dynamically created based on data received from outside data sources, or might originate with the channel developer.
![]() |
To develop a simple Java channel |
Channel development consists of developing, configuring, and publishing (and periodically updating) channels. For the most part, creating a Java channel is much like creating any other type of Java application. What makes it a "channel" is how it recognizes and utilizes its relationship with other IntelliPortal components and other channels. This relationship has two main parts. (1) IMecaApplication is the actual interface that enables a channel to play in the IntelliPortal environment. (2) ITunerEventService is an internal messaging system that lets channels communicate with the client application and with other channels.
IMecaApplicationContext is an interface that gets passed to the channel when it is started by the client application. It makes information about its operating environment available to the channel, including other channels that may be running, and services that are available to the channel.
IntelliPortal has a client shell that is in charge of starting and stopping channels. For this to work properly, the Java channel needs to be organized in a particular way. In the base directory of each channel is a file called properties.txt which tells the client shell about your channel. In it, you specify the channel's main class using the Publisher tool. The main class contains a start() method, which is what the client shell calls to start the channel. (Channels do not use a main( String args[] ) method.) When a channel starts,
You generally begin developing a channel by extending an abstract class called MecaBaseChannel. This class implements the necessary interfaces so that your application will have the functionality described above. The MecaBaseChannel class is provided as a base class and will likely meet the needs of developers building lightweight visible channels. Channels are not required to extend from this base class, rather it is provided as a convenience.
Each channel has a main class that is called using the Publisher tool. There are two basic requirements for this class.
MecaBaseChannel fulfills both of these requirements. It is a default base class that implements IMecaApplication. As such, it provides default behavior for the methods in IMecaApplication and is a complete channel in itself that you can modify with your own functionality. Depending on your requirements, you can use MecaBaseChannel in three ways. How you use or don't use this base channel class is determined by your own functional requirements.
Regardless of how you develop your channel, the following discussion should provide a better understanding of the various components and how they interrelate.
An IntelliPortal Java channel has interrelated components that work together rather than separately. For example, every channel must implement IMecaApplication to act as a channel in the IntelliPortal environment. It must also be registered as a TunerEventListener to be able to communicate with other channels. The following basic components and the fundamental relationships among them are explained on the following pages.
IMecaApplication is an interface to the tuner. It is the application interface that allows applications (i.e. channels) to be aware of the context in which they are executing. A channel must implement this interface to run in the IntelliPortal client. There are four methods in IMecaApplication that are described in detail on the following pages.
The first method called after the default constructor is always setContext( IMecaApplicationContext ). It is called immediately following the construction of the main class of the channel. IMecaApplicationContext is an interface to the tuner that lets channels access services that are available to the client. To use the services, the IMecaApplicationContext must be accessible to any objects that you create.
If you are implementing IMecaApplication, you should save the reference to IMecaApplicationContext as a data member in your class so that you can use it while your channel is executing. You need to save the reference because this class provides the channel with its only means of communicating with the tuner and its services. MecaBaseChannel does this and saves the reference in a protected variable. It also provides the helper method getContext() that a channel can call to retrieve the application context.
MecaBaseChannel also uses the context to get and save references to two commonly used services: the ILayoutService and ITunerEventService.These services are stored in protected variables as a convenience to developers since these two services are present in every implementation of IntelliPortal and will most likely be used frequently. It is unlikely that you will ever need to override this method.
It is important to note that at this point in the channel's life, it is not known whether or not the channel is going to be running. The tuner may have instantiated the channel because an update is available and the channel has been set up to receive notification (see Using Triggers and History). In this scenario, the channel's constructor would get called, then its setContext method, followed by the delivery of an event indicating that an update is available. Therefore, you should not assume at this point that the channel will get started. With this in mind, it is a good idea to defer the construction of any GUI components (or other components related to the normal running of the channel) to the start() method.
When a channel is being started (or restarted after an update), the start() method is called after the channel is instantiated and the IMecaApplicationContext is set. This is the point at which you can code any unique initialization or startup tasks needed by a channel. MecaBaseChannel provides a "bootstrap" routine that does the following:
Most likely, you will want to override this method so that you can at least introduce your own functionality. The easiest way to chain your own routines to the default start() functionality is to call super.start()at the beginning of your own implementation, and then insert your own routines. Keep in mind that calling super.start() will add the MecaBaseChannel panel to the tuner using the ILayoutService. Until you call this method or add yourself directly to the tuner using the ILayoutService, your panel will not be parented. Depending on how you want your channel's functionality activated, you might:
This method is the last method to run before the channel stops and goes away. Override this to kill any threads and dispose of any graphics, windows, or other resources you have opened. The default implementation of MecaBaseChannel unsubscribes the channel from the ITunerEventService, pulls the CHANNEL_STOP trigger, and saves the trigger table if changes have been made. See Using the Trigger Editor to learn more about the CHANNEL_STOP trigger.
The handleEvent( Event e ) method is used to handle a number of channel events that are typically used in connection with channel updates. During an update, the channel is passed an event object and you can query this object for a certain type of event.
Use these events (in conjunction with the entries in the properties.txt file) to control channel behavior at various points in the update process. See Updating Channels for more information on how handleEvent( Event e ) is used. Note that a channel may be started to handle an update without its start() method being called. If an update occurs when a channel is not running, the tuner starts the channel by calling handleEvent() and DATA_UPDATE_EVENT.
Channel events are different than TunerEvents. Use handleEvent( Event e ) to process channel events, particularly updates; use tunerMessage( TunerEvent e ) to process TunerEvents. |
This interface lets channels access services provided by the client application. It encapsulates much standard channel communication and provides additional IntelliPortal-specific methods. It provides access to information about the environment and other channels, and provides access to the client services. It also provides additional IntelliPortal-specific features such as a shared directory across multiple channels. It is the single point of contact among the client, the tuner, and the channel. IMecaApplicationContext has numerous methods, including the following, most of which are self-explanatory.
You always request a service using the method getService(String). The interface com.mecasw.eb.services.IServiceConstants has static string definitions for all available IntelliPortal services. To request a service, use the appropriate string as the argument for the service. See the examples that follow.
import com.mecasw.eb.services.IServiceConstants;
public class Foo extends MecaBaseChannel
{
private ITunerEventService ts;
public void start ()
{
tx = (ITunerEventService)m_context.getService
(IServiceConstants.TUNER_EVENT_SERVICE );
}
}
import com.mecasw.eb.services.IServiceConstants;
public class Foo extends MecaBaseChannel implements IServiceConstants
{
private ITunerEventService ts;
public void start ()
{
tx = (ITunerEventService)m_context.getService (TUNER_EVENT_SERVICE );
}
}
All IntelliPortal channels (Java and HTML) have access to three directories (Base, Data, and Shared) for disk space on the client workstation. All three directories are available through the IMecaApplicationContext object passed to channels.
Base Directory - Each channel has read access to its Base directory. Data received as part of the channel update process or in response to a client request for data (e.g. stock quotes, statement information, etc.) is stored in the channel's base directory when installed.
Data Directory - Each channel has read/write access to its own Data directory that is defined by the client application. This directory is the same each time the channel is instantiated so you can use it to store data that will be needed across channel launches. This directory is for internal channel use only.
Shared Directory - Each channel also has read/write access to a Shared directory which is used for storing data that is available to all channels. You can get this directory in two ways
TunerEvent messaging is also function of IMecaApplicationContext. The TunerEvent messaging architecture lets the client application communicate with channels. The architecture is centered around TunerEvents objects that encapsulate messages and associated data, and the ITunerEventService that receives TunerEvents and routes them to the appropriate objects.
TunerEvent objects have two responsibilities. (1) They publish all event notifications (i.e. TunerEvents) to the ITunerEventService rather than to individual objects. (2) They subscribe with the ITunerEventService to TunerEvents which are of interest to them. With this design, objects can receive only those events they wish to receive and that are relevant to their purpose. Plus, they can receive all such events regardless of where they originate.
For data security, only instances of system classes can be shared between channels. Instances of classes loaded from a channel cannot be shared with other channels. |
IntelliPortal has an interface called TunerEventConstants that includes a number of predefined TunerEvent constants that correspond to the most common TunerEvents. Most of these are used by the tuner to tell channels to perform specific actions. They include channel start/stop, channel subscribe/unsubscribe, and navigation requests. Other TunerEvents encapsulate user actions, such as keystrokes and mouse clicks, and status messages from channels.
As an example, consider a history channel which maintains a running list of all of the channels that a user has viewed during a session. IntelliPortal includes a TunerEventConstant called ENTER_BOOKMARKABLE which indicates that the user has visited a specific location within a channel. So, the history channel would register as listener with the ITunerEventService, subscribing to ENTER_BOOKMARKABLE events. Every time the ITunerEventService receives an ENTER_BOOKMARKABLE event, it forwards it to the history channel. You could also further refine the scope of TunerEvents such that you only get notified of events for a specific channel, and only then when the event details match specified criteria (see Creating Custom TunerEvents for details).
You need to perform the following steps to create and publish TunerEvents. If you extend MecaBaseChannel, they are done automatically.
![]() |
To create TunerEvents |
m_tunerService.publish (new TunerEvent(EVENT_NAME));
ITunerEventService ts = (ITunerEventService) m_context.getService( TUNER_EVENT_SERVICE );
The TunerEvent class includes many constructors that let you bundle other information with the event in a variety of ways. While the previous example used only the event name, you can include up to four string arguments, plus an additional object of whatever type you wish. It also includes get and set methods that let another object extract the additional data (see the javadoc for specific constructors and methods). The full constructor for a TunerEvent is as follows. See the javadoc on TunerEvents for details).
TunerEvent( String eventName, String eventType, String option1, String option2, Object value )
It requires two pieces of code to receive TunerEvents. The first is the subscription request; the second is the implementation of the tunerMessage( TunerEvent e ) method that specifies what the channel should do when an event is received. You need to perform the following steps to subscribe to TunerEvents. Again, if you extend MecaBaseChannel, they are done automatically.
![]() |
To subscribe to TunerEvents |
m_tunerService.subscribe( this, new TunerEvent(EVENT_NAME));
ITunerEventService ts = (ITunerEventService) m_context.getService( TUNER_EVENT_SERVICE );
Then, as part of the tunerMessage( TunerEvent e ) method, specify what to do when the event EVENT_NAME is received. Typically, this method consists of a number of if:else statements which encompass each of the events your channel subscribes to. See tunerMessage( TunerEvent e ).
As noted, the TunerEvent class includes constructors that let you bundle other information with the event. You can use these optional parameters to create what are effectively your own custom TunerEvents. You can construct and publish custom TunerEvents as follows.
m_tunerService.publish(new TunerEvent(PortfolioManager.EVENTS, PortfolioManager.UPDATES, PortfolioManager.VALUE, new Double( currentPortfolioValue ));
This TunerEvent might be used to broadcast the current value of the portfolio. The event name (EVENTS) is a constant from the main channel as is the event type (UPDATES) and option (VALUE). The value of the portfolio is represented by a Double since you cannot pass primitive types.
m_tunerService.subscribeTunerEvent(this, new TunerEvent(PortfolioManager.EVENTS));
This statement subscribes to all the events of the PortfolioManager, regardless of their type or the data they contain.
m_tunerService.subscribe(this, new TunerEvent(PortfolioManager.EVENTS, PortfolioManager.UPDATES));
This statement subscribes to only those TunerEvents from the PortfolioManager that deal with updates.
m_tunerService.subscribe(this, new TunerEvent(PortfolioManager.EVENTS, PortfolioManager.UPDATES, PortfolioManager.VALUE));
This statement subscribes to only those events from the PortfolioManager that deal with updates in values. This statement makes use of the event, type, and option1 filters of the TunerEvent. You could further qualify it by adding a second option filter but you cannot filter on the Object field. See the javadoc for a more detailed explanation.
The MecaBaseChannel base class is a shell channel that provides a complete implementation of an IntelliPortal channel including update processing, and loading and processing triggers. It supports history and bookmarking via triggers, and it locates the appropriate panel that it is designed to play in. You can use the default behavior (as explained on the following pages) of MecaBaseChannel methods, you can override them, or you can create your own. The only thing you need to do is add your own functionality and logic, and override the methods that are not appropriate. The following sections describe some of the likely hookpoints for customizing it.
MecaBaseChannel is a default implementation of IMecaApplicationContext. MecaBaseChannel provides a TunerEventListener and an overridable method. MecaBaseChannel automatically listens for many of the things that you want to create a listener for (or be a listener for). It calls a virtual method that you can override or create your own behavior for. Some of the more significant methods in MecaBaseChannel are explained below. The default behavior of these methods is described on the following pages.
Many of the methods described here refer to triggers and TunerEvents, which are more fully described in TunerEvent Messaging and Using Triggers and History. |
This method makes a channel a TunerEventListener. This is one of the two basic requirements for a channel. (It must also implement IMecaApplication.) It gets called by the ITunerEventService when it has a TunerEvent that your channel has subscribed to, passing to it the subscribed event. Typically this method consists of a series of case:switch or if:else routines.
Each TunerEvent that your channel subscribes to must be handled here. The default implementation handles the TunerEvents subscribed to in the start() method. These are
If you are extending MecaBaseChannel and overriding the tunerMessage method, be sure to call super.tunerMessage( TunerEvent ) since the parent's implementation redirects the events above to the appropriate methods (which you can also override).
This method gets called whenever the channel's display panel is brought to the foreground. For this to occur, the following must happen:
The default implementation of MecaBaseChannel does this, but if you override either start() or tunerMessage( TunerEvent e ) it might not, unless you explicitly provide for it. The default implementation of announceMe() simply gets the current channel location and pulls any trigger that might be associated with it.
This method is called when updated data becomes available and before it is installed on the client machine. Use this method to specify how the data will be installed. The default behavior is to read the properties.txt file and follow the instructions specified with the update.action parameter. The String parameter indicates the topmost subdirectory of the channel that will receive the update. The default implementation of notifyAvailable( String dir ) is to store the details of the update (an IUpdates instance) in a protected variable.
This method is called after updated data is successfully installed. Override if you want to do anything after the data is installed or with the new data (e.g. display it on the screen). The default implementation merely checks to see if there have been any updates to the channel's Trigger directory, and if so updates the TriggerTable.
When the client application is about to be shut down, a VETOABLE_STOP event is broadcast. If your channel is engaged in an operation that should not be interrupted (e.g. reading information from a database), this event is designed to give the channel an opportunity to veto the shutdown request. This boolean method is provided as a convenience method to handle this event.
![]() |
To use a VETOABLE_STOP |
Note that in the default MecaBaseChannel implementation, the return value of vetoStop() is actually wrapped in the return object for tunerMessage( TunerEvent e ), as follows:
[tunerMessage( TunerEvent e )]
if (evt.getOption1().equals(VETOABLE_STOP))
{
Boolean b = vetoStop();
return(b);
}
The default implementation of vetoStop() does nothing but returns Boolean.FALSE.
The easiest way to create a channel is to extend MecaBaseChannel. The following code shows a "Hello World" example that is a complete and self-contained channel. It has an application context, a layout, and its own functionality (it prints "Hello World"). It is very simply created by extending MecaBaseChannel. By default, this interface satisfies the two basic requirements for a Java-based channel: (1) it implements IMecaApplication and (2) it registers itself as a TunerEventListener. If the default behavior of MecaBaseChannel is adequate, you can extend directly from it and greatly simplify channel development.
import java.awt.*;
import javax.swing.*;
import com.mecasw.eb.channels.MecaBaseChannel;
public class HelloWorldPanel extends MecaBaseChannel
{
public HelloWorldPanel ()
{
}
public void start()
{
super.start();
System.out.println( "channel started...\n" );
add (new JTextArea ("Hello, World!"));
return;
}
}
You can also create your own channel class from scratch without extending MecaBaseChannel. If you do not extend MecaBaseChannel, you still have to implement IMecaApplication and make your class a TunerEventListener (by implementing IMecaApplicationContext and calling the ITunerEventService).
The following code shows a "Hello World" channel that has been created by implementing IMecaApplication (and by default IMecaApplicationContext). It does not extend MecaBaseChannel. See the comments in the listing for more information and examine the following code closely. It shows much of the behavior that is performed by default if you do choose to extend MecaBaseChannel.
import java.awt.*;
import com.sun.java.swing.*;
import com.mecasw.intf.*;
import com.mecasw.eb.intf.*;
import com.mecasw.eb.services.*;
public class HelloWorld implements IMecaApplication
{
protected IMecaApplicationContext m_context;
protected JPanel m_parentPanel;
/**
* Start the application. This method is called by the
* tuner to notify that the application is started.
*/
public void start ()
{
ILayoutService lay = null;
IServiceProvider isp = m_context.getServiceProvider ();
try
{
lay = (ILayoutService)isp.getService (
IServiceConstants.LAYOUT_SERVICE );
m_parentPanel = lay.getNewPanel (m_context.getParameter
("Location"), m_context.getChannelName ());
m_parentPanel.setLayout (new BorderLayout ());
JTextArea t = new JTextArea ("Hello, World!");
Font f = new Font ("Serif", Font.ITALIC, 32);
t.setFont (f);
t.setForeground (Color.blue);
m_parentPanel.add (t, BorderLayout.CENTER);
m_parentPanel.setName (m_context.getChannelName ());
lay.showPanel (m_context.getChannelName ());
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
m_parentPanel.validate ();
}
/**
* Stop the applicaton. This method is called by the tuner
* to notify the application that it will be killed. To stop
* an application call stop() in the ChannelApplicationContext.
*/
public void stop ()
{
// clean up after the channel here
}
/**
* Handle an event. Events are posted to the application to
* notify applications of changes in its status.
*/
public boolean handleEvent (Event evt)
{
switch (evt.id)
{
case DATA_AVAILABLE_EVENT:
System.out.println (" Data Available");
return true;
case DATA_UPDATE_EVENT:
System.out.println (" Requesting Update");
return false;
case DATA_INSTALLED_EVENT:
System.out.println (" Installed!");
return true;
case DATA_NOTIFY_EVENT:
System.out.println (" Notify INACTIVE.");
return true;
case DATA_NONE_AVAILABLE_EVENT:
System.out.println (" No Data Available");
return true;
}
return (false);
}
public void setContext (IMecaApplicationContext context)
{
m_context = context;
}
}
Updating a channel simply means that new files are copied to the channel base directory on the client machine. There are three ways to update a channel and they apply to both static and dynamic channels. Also, note that one channel can automatically initiate an update in other channels.
| Channel Update Method | Description |
|---|---|
| Automatically | (This is the only update method for HTML channels.) Channel updates can be scheduled in the properties.txt file. You can specify separate update frequencies for when the channel is running or is not running. These updates are executed automatically and are transparent to users. You can also turn off automatic updates (see Property Files for more information). |
| Developer-initiated | You can also initiate an update from the developer's side, so that if you create new channel content you can transmit it immediately to all subscribers regardless of the update schedule. (It is installed on clients' machines the next time they connect.) |
| Channel-initiated | A channel can always request an update at any time, by calling the context.update()method. For example, you might set up a channel so that an update request is issued after the user enters some data in a field. |
All channels have a data stream associated with them called their profile to which the channel can write. When the tuner sends an update request to the transmitter, it includes the profile stream, giving the channel a mechanism to add data to any update request. When the tuner is about to make an update request, it sends the channel a DATA_UPDATE_EVENT that gives the channel the opportunity to write to the profile stream any data it has been holding. The handleEvent( Event e ) method in MecaBaseChannel processes this event although you can override the default implementation.
Note that you will still receive a DATA_UPDATE_EVENT even if the update request originated within the channel. If you want to write data to the profile, you can either do this when you call context.update(), or after you receive the DATA_UPDATE_EVENT. When the updated files (if any) have been received by the tuner from the transmitter, the tuner notifies the channel that an update has occurred. What happens next is determined by entries in the properties.txt file and by whether or not the channel is running.
The tuner sends a DATA_AVAILABLE_EVENT to the channel when it receives the updated data. This is received by the handleEvent( Event e ) method, which calls the notifyAvailable( String dir ) method. This does the following.
After the data is installed, the tuner sends the channel a DATA_INSTALLED_EVENT. This is also processed by handleEvent( Event e ), which calls the channel's notifyInstall( String dir ) method. If the channel update did not return any new data, the tuner sends handleEvent( Event e ) a DATA_NONE_AVAILABLE_EVENT. No action is taken as a result of this event. See Extending MecaBaseChannel for more information about these methods.
The tuner reads the install.inactive entry in the properties.txt file and interprets its value as follows:
The DATA_NOTIFY_EVENT is directly handled by the handleEvent( Event e ) method. If this method returns TRUE, the channel's start() method is called; if FALSE, the stop() method is called. The default implementation of handleEvent( Event e ) in MecaBaseChannel returns TRUE for this event. You can override this behavior in your channel class that extends from MecaBaseChannel. For example, you might want to look at the data first, returning TRUE or FALSE depending on what you find.
Many channel applications require the ability to process a request for data that is customized for a specific user or group of users. Such data is supplied as part of the update process. This data can be customized for specific clients in two ways. The end user might prepare a one-time request for specific data, where the customization information originates with input at their machine. Examples would be inputting a series of ticker symbols for news or quotes, or clicking on a button to see current mortgage rates.
A data request might also be customized for a specific user or group of users, where the customization information comes from an external source such as a database of customer profiles. Examples of this would be targeted advertising, or news that is only relevant to certain people. Both scenarios require the channel to assemble and transmit dynamic files. Files which are to be transmitted to all subscribers are referred to as static files. Note that an update of dynamic files might also include static files as well. A request for data is processed as follows:
![]() |
To create a dynamic update request |
The data a channel needs might consist of several different types of data. For example, a portfolio channel might be responsible for returning charts, current prices, and other data and news. You would identify these as different groups within the channel, each with a distinct name: "Charts," "News," "Quotes," etc. Group names are formalized with the Server Administrator tool. Then use the methods in the IRequestBufferService to build a properly-formatted byte array that can be added to the profile stream.
![]() |
To build a formatted byte array |
The add() builds a buffer with the successive chunks of data. The examples here pass three strings (group name, a series of ticker symbols, and the delimiter character that should be used to parse the chunk of data). add() has a number of different implementations than you can use. (See the javadoc for details.)
IRequestBufferService bs = ctx.getService (IServiceConstants.REQUEST_BUFFER_SERVICE) IRequestBuffer rb = bs.getBuffer (); rb.add("News", "IBM~SUNW~AMZN~MSFT", "~"); rb.add("Quotes", "IBM~SUNW~AMZN~MSFT", "~"); rb.add("Charts", "IBM~SUNW~AMZN~MSFT", "~"); bs.commitBuffer (rb, ctx);
When you commit an IRequestBuffer using the commitBuffer() method, the buffer remains in existence and is sent to the server for all updates unless the buffer is explicitly cleared or overwritten with a new call to commitBuffer(). To clear the buffer, call commitBuffer() with null for the IRequestBuffer argument: requestBufferService.commitBuffer(null, context);
Use the Server Administrator tool to describe the process by which the server will parse the data from the profile log. For each group (e.g. News, Quotes, etc.) that you identified in step one in the previous section, you need to provide the following information in the Server Administrator.
You also need to specify the name of an adapter which is the class that will actually get the data from the data source. The server uses the above information to create an object with a properly formatted target and pass it to an instance of the adapter which opens a connection to the data source, manages the connection, and returns the data. If the proper adapter does not already exist on your system, you must write Java code to create it.
Once the requested data is received by the server from the data source, the following process occurs.
Another way to take advantage of TunerEvent messaging and other services in the tuner is by using triggers. Triggers associate actions like the production of TunerEvents with the value of a key field. Triggers are a convenient way to cause changes in other channels based on criteria that can be set in advance. For example, you can create a trigger that automatically starts a particular channel whenever a certain HTML page is displayed. See Using the Trigger Editor for a complete description of how to use this tool.
Triggers produce the same result as publishing and subscribing to TunerEvents or using the services available in the tuner directly. The key advantage of triggers is that the actions associated with the triggers exist outside the channel's codebase and are managed in a separate object called a TriggerTable. The abstraction of the triggers from a channel's codebase enables non-Java channels like HTML channels to interact with the tuner, services, and other channels without having to include custom applets within the HTML content itself.
The Trigger Editor tool provides a GUI front-end to the TriggerTable that can be used to build triggers and keys. Once you have coded a channel with trigger capabilities, you can create and manage links and dependencies between channels without further Java programming. This also allows developers of HTML channels to take advantage of the TunerEvent functionality without coding any additional tags or applets in their channel content.
The trigger mechanism is simply a vehicle for producing TunerEvents or interacting with the services available from the context. |
As shown below, the trigger architecture has three levels of component classes, the lowest being TriggerItem.
A TriggerItem is class whose purpose is to perform some type of action when "pulled" by calling its pull() method. The exact nature of this action depends upon the value of the type field and the value of the delegate field. All TriggerItems have the three properties shown in the table.
| TriggerItem Property | Description |
|---|---|
| ID | A unique identifier for this TriggerItem. Must be unique within a trigger such that no two TriggerItems within a trigger have the same ID. Used to set dependency (see Setting Dependencies below). |
| Expiration time | Either a specific time, or you can use a predefined constant to set the TriggerItem to never expire or to expire immediately when pulled. For more details, see the Expirable interface. |
| Type | Determines what happens when the TriggerItem is pulled. ENTER_BOOKMARKABLE - notifies the service that the IBookmarkable object has entered a bookmarkable state. EXIT_BOOKMARKABLE - notifies the service that the IBookmarkable object has entered a non-bookmarkable state. TUNEREVENT_PRODUCER - capable of producing a TunerEvent. SERVICE_USER - delegate action to IServiceUserObject RUNNABLE - delegate action to Runnable object. HISTORY_ENTRY - delegate action to IHistoryService. |
A trigger is a Vector of TriggerItems accompanied by a key. The Trigger class has its own pull() method which pulls each of its TriggerItems when called. The two most important tasks at the trigger level are setting the trigger's key and setting dependencies among the TriggerItems.
If you don't care about the order in which TriggerItems get pulled within the trigger, skip this paragraph and go to Keys, Tokens, and Cleaning Triggers below. |
Use the Trigger Editor to associate a TriggerItem with one or more other TriggerItems in the same trigger such that the TriggerItem will not fire unless the other selected TriggerItems fire successfully. For example, if a Trigger has TriggerItems A, B, C, and D, you could specify that C will not fire unless A fires successfully, and D will not fire unless B and C fire successfully.
For each TriggerItem that depends on another, the Trigger constructs a vector which articulates the dependencies and is stored in a hashtable. You can use the getDepend() method to get the table itself or the dependency vector for any of the TriggerItems. You could also use setDepend() to set dependencies (see the javadoc for specific constructors).
Filenames and URLs are frequently used as trigger keys. However, since you can't know the absolute path of the channel's files, IntelliPortal lets you construct trigger keys with three different tokens (BASE, DATA, and SHARED) which correspond to the channel's base, data, and shared directories. For example, you might specify an HTML page as BASE\filename.html. See the javadoc for ITriggerCleaner for the constants you need to use.
IntelliPortal automatically replaces the tokens with the proper directory strings in a process known as cleaning. A TriggerTable automatically cleans its triggers when loaded. The cleaning is performed by an object which implements the ITriggerCleaner interface; such an object is passed to the TriggerTable when it is instantiated. Often, this will be the ITriggerTable's channel. MecaBaseChannel has an inner class that implements the ITriggerCleaner interface, so if your channel extends MecaBaseChannel you are done.
In order to use the default triggers that are provided with IntelliPortal, you must publish the sample trigger file. If the sample trigger file is published with the channel, it will have default triggers with the following keys: CHANNEL_START, CHANNEL_STOP, and DEFAULT. The first two triggers are automatically pulled when a channel starts and stops. These are provided in the default implementations of start() and stop() in MecaBaseChannel. In the default configuration, the CHANNEL_START trigger fires an ENTER_BOOKMARKABLE event and the CHANNEL_STOP trigger fires a EXIT_BOOKMARKABLE event. You can add to or change this behavior with the Trigger Editor. The DEFAULT trigger is pulled when the TriggerTable is searched for a trigger that is not found (except for CHANNEL_START and CHANNEL_STOP). Use the Trigger Editor to add TriggerItems to this trigger as necessary.
The TriggerTable is basically a hashtable of triggers for a particular channel. The TriggerTable class also includes a number of methods which are used by the channels as the primary interface to trigger functionality. The channel will typically instantiate a TriggerTable and expose one or more services to the TriggerTable as well as passing in one or more references via a IGenericService. MecaBaseChannel exposes the ITunerEventService, IBookmarkService, and IHistoryService by default and also provides references to ITriggerCleaner and IBookmarkable objects via an IGenericService.
Although the triggers themselves can be created and modified with the Trigger Editor, you need to have the channel check for the triggers at the appropriate time. With HTML channels, this is easy because the Java class that renders the pages in the IntelliPortal frame automatically checks for triggers each time a new page is displayed; all you need to do is publish the channel normally, and then use the Trigger Editor to create the triggers, keyed to the URLs of the channel's pages. With Java channels, it's slightly more complicated.
![]() |
To check the TriggerTable (Java channels) |
There several ways to accomplish the second step. MecaBaseChannel includes a convenience method called pullTrigger( String ) that you can use for this purpose and that is already used by other MecaBaseChannel methods. In addition to a String which contains the key value, it also accepts a TunerEvent that you can take advantage of if you override the method. Use this method as follows:
try
{
pullTrigger(key_value, evt); //evt can be null if desired
}
catch(TriggerException e)
{
[handle exception]
}
If you choose not to use pullTrigger, you can pull the trigger yourself by getting a reference to the TriggerTable and calling its pull() method: getTriggerTable().pull(key_value);
The TriggerTable also includes the boolean method containsTrigger() which you can use to find out whether a trigger or key exists in the TriggerTable without pulling it. You can call this method passing it either a key value or a trigger itself.
A bookmark is a pointer to a channel in a particular state or condition. Bookmarks let users mark a specific location in a channel that can be revisited later. As a channel developer, you decide which parts of a channel (e.g. which HTML pages) are "bookmarkable." When a bookmark is restored, the tuner starts the channel (or brings it to the front) and uses the bookmark object to restore its previous location (if getBookmark() and restoreBookmark() are implemented appropriately).
Any channel can be bookmarked to its initial state. The bookmark facility is accessed by channels via the com.mecasw.eb.services.IBookmarkService interface. An object that implements the bookmark service interface is instantiated during tuner startup. It is then added to the IServiceProvider for channels to access before any channels are actually started.
The IBookmarkService API lets channels add or remove bookmarks from the bookmark list as well as to set a flag indicating that the channel is bookmarkable. This flag is set by calling the EnterBookmarkable( IBookmarkable ) method on the bookmark service and passing in a reference to the bookmarkable object. When the user hits the bookmark button, the bookmarkable object that was passed in via the enterBookmarkable method is used to construct a bookmark.
When the channel enters a non-bookmarkable area then the channel should call the exitBookmarkable( IBookmarkable ) method in the service, again passing in a bookmarkable object. When the exitBookmarkable method is called and the bookmarkable object is the same as the one passed with the enter method, the bookmark button on the client application is disabled, indicating that the current content is not bookmarkable.
The tuner instantiates the IBookmarkService class and installs it as an IService into IServiceProvider. The IBookmarkService starts before any channels are running. The BookmarkService class registers itself as a listener for the starting and stopping of the BookmarkChannel. If any calls to the BookmarkService methods are made before the BookmarkChannel starts, these calls are queued. Once the channel starts, any commands in the queue are delivered to the BookmarkChannel in the order in which they were received. This is accomplished through the use of the TunerEventPublisher class. Once the BookmarkChannel is started, all calls to the BookmarkService are delivered to the BookmarkChannel via TunerEvents.
Channels that want to use the IBookmarkService to add or remove bookmarks or to report that they have entered or exited a bookmarkable state simply get a reference to the IBookmarkService from the IServiceProvider available from their IMecaApplicationContext.
IBookmarkService bs = (IBookmarkService) context.getService( BOOKMARK_SERVICE ); bs.add( myBookmark ); bs.remove( myBookmark ); bs.enterBookmarkable( myBookmarkable ); bs.exitBookmarkable( myBookmarkable );
Whenever a channel enters a bookmarkable state, it should call the enterBookmarkable method on the IBookmarkService. This tells the BookmarkChannel that the channel has entered a state that can be bookmarked and enables the BookmarkButton. This allows the end user to hit the bookmark button to save the state of the channel in a Bookmark object which is then be added to the BookmarkList. Calling the exitBookmarkable method on the IBookmarkService has the opposite effect: the bookmark button is disabled until it receives an enterBookmarkable call.
The BookmarkChannel saves the IBookmarkable reference that it is passed with the enterBookmarkable and exitBookmarkable methods. When the user clicks on an enabled bookmark button, an ActionEvent is fired which notifies the BookmarkChannel. The BookmarkChannel then uses the IBookmarkable reference it received from a previous enterBookmarkable call to create a Bookmark object and adds it to the BookmarkList. Thus the creation of the Bookmark object is deferred until the user actually clicks on the bookmark button.
The recommended base class for channel developers (MecaBaseChannel) contains an inner class which provides a basic implementation of the IBookmarkable interface. This implementation uses the channel's TriggerTable to determine whether or not the channel is bookmarkable. This is accomplished by using the channel's current location (as returned by the String getLocationInChannel() method) as a key into the TriggerTable. If a Trigger exists at the key which contains a TriggerItem of type TriggerItem.ENTER_BOOKMARKABLE, the channel is considered to be in a bookmarkable state.
The use of the inner class provides channel developers with a means to extend the class and override one or more the methods to provide their own implementation. Bowser3 (the default browser for HTML channels) extends the Bookmarkable inner class in MecaBaseChannel to return the current URL for the location in the channel and to restore a Bookmark object using the bookmark's tagString field to load a new URL.
HTML channels are those whose functionality is limited to displaying HTML pages. HTML channels are similar to a website, except that the entire channel is downloaded and stored as a whole rather than page by page. HTML channels have two main components. One is the HTML pages themselves. The other is a Java channel "wrapper" that renders the HTML pages in the client shell and administers the IMecaApplicationContext and messaging services as described earlier. This Java channel lets you display HTML pages directly in IntelliPortal so that you don't need a separate browser program.
![]() |
To create HTML-based channels |
There are two ways to build HTML channels. You can build a channel for offline viewing, in which case all HTML pages are copied to the client machine. You can also build an online channel that only consists of a pointer to a URL. (In this case, you can skip step one above). When you click on the online channel, the system opens an Internet connection and displays the indexed page.
In addition to standard properties that all channels have (see Property Files), HTML channels have an additional parameters.txt file in the channel's base directory. This parameters.txt tells the channel where to find the first HTML page. This file is required in addition to the properties.txt file that is required for all channels. It is created by the Publisher when you publish a channel and can be modified with a text editor. The parameters.txt file has the following contents.
loadmode=channel | web defaultURL=
You can also use the parameters.txt file to customize the browser navigation controls for specific HTML channels. You can add statements to parameters.txt that override the default statements in the browser.ini file. The statements you add are the same as those in the browser.ini file; you simply add statements for the controls you want to change. When the channel runs, the browser uses the statements in the parameters.txt file rather than the default statements in the browser.ini file. See the Client Customization Guide for complete details.
An advertisement channel, as shown in the example, lets you group ads together into an "ad queue" and lets you manage the rotation of the queue. Upon connection, the server supplies the ad queue to the client. The advertisements displayed on the client can be static, or can trigger another channel or jump to a web site if you click on the ad.
On the client side, the ad channel can be assigned to any available (and appropriate) panel in the client layout. The channel fetches and displays the appropriate ad from the queue for a configurable duration. This duration is the amount of time (15 seconds minimum) to display each ad before showing the next ad in the queue. Additional properties determine how channels are mapped to categories of ads.
On the server side of the channel, all ad files (JPEGs, GIFs, or animated GIFs) must reside in one directory. This directory also includes a catalog of the all ads in the directory. For each ad there are one or more categories (set in advance) that are used to construct bookmarks if you click on the ad. Any update connection fetches all the ads that are currently in the server ad directory.
This section explains how to build an ad display channel from scratch. It explains how to set up the required files and how to configure the client and the server. An ad sample channel is included with IntelliPortal. See the AdChannel for details. |
All ad files are stored in a directory on the server and the pre-built FileReaderAdapter is used to retrieve and send them to the client at update time. There is no caching of ads on the server; this means that any update connection always gets all the ads that are currently stored on the server. The ad channel can be assigned to any available panel in the client layout. Always test the channel to be sure that it can properly display all images, animated GIFs, etc. An ad channel will not work if the client panel is not large enough to accommodate the images. See Client Layout for more information about panels.
The ad channel must be designated as Autorun when published so that it runs in the designated panel at startup. It should also register as a listener for TunerEvents relating to TO_FRONT and SHOW_PANEL so it knows what channel is currently in front and visible to the user. An ad channel should typically respond to only those channels that are displayed in the main content panel. Similarly, an ad change is triggered whenever a new channel is brought to the front. See the Java code for the AdChannel sample for details on how this is all accomplished.
![]() |
Use the following basic steps to build and configure an ad channel |
Assuming you have created an ad channel, the next step is to create the server files. The ad channel requires certain files on the server. Theses include (1) your ad images; (2) a category file; and (3) a criteria file. These later are typically called categoryfile and criteriafile. They are referenced on the client side in the ad channel's properties.txt file. They are comma-delimited ASCII files with no embedded spaces. For best results, modify the pre-built files that are included with the sample channel. These files are located on the installation CD-ROM in the directory misc\channels\SampleChannels\AdChannel. You can modify them with any editor and add them to the appropriate directory on the server. All images (JPEGs, GIFs, animated GIFs, etc.) must be present in the same directory as the category and criteria files.
| categoryfile | Associates a channel with a category. Only one category is associated with each channel. |
| criteriafile | Associates ads with categories. Multiple categories can be associated with one ad. |
When creating these files, keep the following concepts in mind: the category file associates channels with categories; the criteria file associates categories with specific ads. Together, they determine which ads are displayed for a given channel.
This file maps channels to developer-defined categories. Each category is unique and cannot be duplicated. There is only one category per channel which is the ad queue for that channel. When the channel starts, the ads in that category are displayed in the ad channel panel. The file has the following comma-delimited format.
<categoryname>,<channel name>
In the following example, the transactions category is associated with the Register channel.
transactions,RegisterChannel accounts,AccountListChannel departments,CategoryListChannel
This file is a catalog that completely describes each ad that is available for display. In addition to name and filename, it has a target URL that is used to construct a bookmark that is invoked when you click on the ad. It also lists the categories in which the ad is included. This file also determines the order in which ads are displayed. The ads are displayed sequentially in the order in which they are listed in the file. It has the following comma-delimited format.
<ad name>,<ad file name>,<target URL>,<category1>,...<category-n>
In the following example, the ad name diamonds displays the animated diamonds.gif image, and links to diamonds.com when clicked. It displays ads in the transactions and general categories.
diamonds,diamonds.gif,http://www.diamonds.com,transactions medical,medical.gif,http://www.medicalshopper.com,accounts drugstore,drugstore.gif,http://www.drugstore.com,departments,general trade,ameritrade.gif,http://www.ameritrade.com,general
Note that there is no general entry in the category file. Ads with the general category are only displayed for channels that do not have an associated category. For example, if you publish a new channel (and do not update the category file) it gets all ads in the criteria file that have the general category. This means that if you set all ads to general and leave the category file empty but still present, all the channels get all the ads.
The ad channel and the InvisibleHtml channels must both be configured and published for an ad channel to run properly.
If you published and are using the AdChannel sample channel, the default properties were published with the channel. You only need to set properties and re-publish if you change these default properties. These properties include the following.
![]() |
To change ad channel properties |