Chapter 10

Creating Application Channels

Although it is entirely possible to create a Marimba Castanet channel with an HTML Web site, a precompiled applet, or an already-built Bongo presentation, true channels are written and compiled in Java and implement several interfaces and events that allow them to coexist with the transmitter and the tuner. There's no other way to allow a channel to accept dynamic updates, to save local files to the user's channel directory, or to send feedback data to the transmitter. To take full advantage of the features Castanet gives you in channel development, you have to use an application channel.

In this chapter, you'll take the first step toward creating real Castanet channels. The channels you create in this chapter will have all the earmarks of a channel, including using the important interfaces and classes in the marimba package and implementing the correct methods and events in your Java code. In the following chapters, you'll build on what you learn in this chapter to add updating and other features.

Working with Java

For this chapter, and for the remainder of this book, you'll be working almost exclusively with the Java language to create full-featured Castanet channels. Beyond this point, things are going to get fairly technical. So before you get in too deep, make sure that you already have the following tools and skills:



Note

If you use a Java development environment other than Sun's JDK, Marimba recommends you recompile your files with the JDK before publishing them. There have been instances of channels that have thrown Class Not Found exceptions when compiled with third-party development environments that vanished once the code was recompiled.


If you're missing any of this background, you definitely should become familiar with Java before going on.



Note

A great place to start learning Java is the book Teach Yourself Java in 21 Days, which I also wrote, and is published by Sams.net Publishing. The text of this book in HTML form is contained on the CD that came with this book.




Note

The 1.0 version of Castanet works only with Sun's 1.02 JDK, not with the newer JDK 1.1. Although future versions of Castanet will support the JDK 1.1 features, for the purposes of this book, you should make sure that you're running a 1.02-compatible compiler and bytecode interpreter.


The marimba.zip and bongo.zip Libraries

The classes and interfaces you'll be working with in the next couple of chapters are part of the Marimba class libraries. They were installed when you installed the Castanet Transmitter and Bongo. After you start writing applications in Java that use any of the features of Castanet channels, you must make sure that your Java development environment knows about these libraries, usually by editing your CLASSPATH variable or setting the preferences to include these libraries.

The two Marimba class libraries are stored in the files marimba.zip and bongo.zip. Whether you need one or both depends on the sorts of channels you plan to create:

Add the directory paths to each of these files to your CLASSPATH, or configure your development environment to find these files.



WARNING

Be careful which marimba.zip you use; the marimba.zip file in the transmitter installation is different from the marimba.zip file in the bongo installation. (It's a superset of the transmitter's version that includes the classes that let you use presentations in channels.) If you start out not using bongo for your channel and then switch, you'll also need to switch marimba.zip files. Or, avoid the problem altogther, install Bongo, and use the Bongo version all the time.


The marimba.zip and bongo.zip files provide several packages for general and utility classes you can use while developing your own channels. A full description of these packages is contained in Chapter 14, "An Overview of the Marimba Classes." You'll use the following packages most often:



Note

In addition to the class libraries, the Bongo and transmitter distributions come with source for many of these classes. Exploring the source can often help you figure out what's going on in a class or help you figure out what you want to do. Feel free to examine those source files as you create your own channels.

In addition, the API documentation for these classes is available on Marimba's Web site (http://www.marimba.com/doc/) or as channels from trans.marimba.com. (They're called Bongo doc and Transmitter doc, respectively.)


Anatomy of an Application Channel

Creating application channels in Java is quite similar to creating applets with Java's AWT. If you've any experience with the former, then channels aren't going to seem all that different. Many of the basic methods are the same, and the event and security models are nearly the same as with applets as well. Some basic differences do exist, however.

The heart of all application channels is the Application interface in the marimba.channel package. The Application interface provides four important methods for channels:

I'll cover the first three methods in this chapter in some detail and touch on handleEvent() as it's used to interact with presentation widgets. In Chapter 11, "Managing Updates and User Information," you'll learn in detail about update events you can process inside handleEvent().

To create a channel, your main channel class can inherit from any class (usually from a standard Java class like Frame) but must implement the Application interface. A basic class template for a channel, then, might look something like the one shown in Listing 10.1. (You'll learn more about the various parts of this template as the section progresses.)

Listing 10.1. A channel template.

import marimba.channel.*;



public class MyChannel extends Frame implements Application {



ApplicationContext context;



public void start() {

   // ... initialize the channel

}

public void stop();

   // ... clean up after the channel

}



setContext(ApplicationContext context) {

   this.context = context;

}



public boolean handleEvent(Event evt) {

   return super.handleEvent(evt);

}



}

The start() and stop() Methods

The two methods that drive a channel's life cycle are the start() and stop() methods.

The start() method is called when the channel first starts running. Unlike the start() method inside an applet, the channel's start() method is called only once, to initialize the channel. It's therefore more analogous to an applet's init() method or an application's main(). Here's the basic template for the start() method:

public void start() {

   // ... initialize the channel

}

The stop() method, analogously, is called once when the channel is about to be destroyed. Inside stop(), you add any code to clean up after your channel, including closing windows, killing threads, and releasing any other objects for garbage collection. You might also want to save any state for the channel or reset global values.

public void stop();

   // ... clean up after the channel

}

The setContext() Method and the Application Context

If the Application interface is the heart of the channel, the application context is its brain. The application context is, most simply, an object that implements the ApplicationContext interface, also part of the marimba.channel package. What the application context actually provides, however, is a set of methods for interacting with the tuner, finding out information about the channel and its properties (for example, its parameters or how often it's been configured to do updates), and to write log and property files for use by transmitter plug-ins. Chances are good that, at some point in the lifetime of your channel, you'll want to get access to this information.

When your channel is first executed by the Castanet tuner, the setContext() method is called just before the start() method is called. The setContext() method is called with an existing ApplicationContext object that contains the channel's application context. In the body of your setContext() method, you should store that context somewhere so that you can use it later. A typical template for setContext() inside your channel might look something like this:

setContext(ApplicationContext context) {

   this.context = context;

}

With the application context stored in the context instance variable, you can then call its methods by referring to that variable.

The handleEvent() Method

Worth mentioning at this point is the handleEvent() method, which is the fourth method the Application interface provides. handleEvent(), like the standard AWT handleEvent() in java.awt.Component, is used to manage user events that occur inside the application. Inside handleEvent(), you should trap any typical events (mouse clicks, key presses), but also manage update events (which you'll learn about in the next chapter).

With normal Java applications, you would test for the WINDOW_DESTROY event and then call System.exit(). For channels, you should call the stop() method on the application context, which manages the existing process (including calling the stop() method for the channel). Here, then, is a basic template for handleEvent():

public boolean handleEvent(Event evt) {

   switch (evt.id) {

      // ... trap different events



      case WINDOW_DESTROY:

         context.stop();

         return true;

   }

   return super.handleEvent(evt);

}

Useful Classes for Creating Channels

Implementing a basic framework for an application channel–or converting a Java application to a channel–doesn't require a whole lot of work. The simple steps you learned in the preceding section are really all you need to follow to get your Java application to work in the channel mechanism. But before you actually start mucking with code, you should know about several classes in the marimba.channel package that make this job even easier.

The ApplicationFrame and ApplicationPlayerFrame classes contain a basic implementation of everything you need to run a basic channel, including basic implementations of start(), stop(), setContext(), and handleEvent().

ApplicationFrame is a subclass of the AWT class Frame, which implements simple default behavior for all the basic methods, and contains a context variable to hold the context. In fact, the framework in Listing 10.1 is similar to the one provided by the ApplicationFrame. If your Java application normally subclasses from Frame (to put up a window and use AWT components), subclassing from ApplicationFrame instead gives you all the additional channel behavior you need.

ApplicationPlayerFrame is a special class for incorporating a Bongo presentation into a Java application that is also a channel. This latter case is one of the easier ways to construct channels by tying together Bongo for the presentation, the Application interface and Application context for the channel behavior, and your own Java code for the meat of the channel. ApplicationPlayerFrame inherits from the Bongo class PlayerFrame–a special class that the tuner uses to play presentations as channels. PlayerFrame, in turn, inherits from jav.awt.Frame. So with ApplicationPlayerFrame, you have access to the full AWT hierarchy as well as Bongo widgets.

If you subclass either of these classes for your own channels, don't forget to call your superclass's start(), stop(), and setContext() methods, if you override them.

You can find more information about ApplicationPlayerFrame in the section "Creating Channels That Use Bongo Presentations."

Using the Application Context

In the setContext() method for your application channel, the current application context is usually assigned to an instance variable called context. With that context object in hand, you can call various methods that let you get information about the channel and perform tuner operations such as requesting a channel update or a restart. You'll learn about many of the context methods as we proceed through this chapter and the next, but Table 10.1 contains a quick summary of many of the more important methods so you know the sorts of things you can do from inside your own channel:

Table 10.1. ApplicationContext methods.

Method name What it does
channelFileExists(String) Returns true if the given relative path exists in the channel.
getChannelName() Returns the full name of the channel.
getChannelStatus(String) Returns the current channel status ( one of unsubscribed, subscribed, or running).
getParameter(String) Returns the value of an application parameter, as stored in the properties.txt or parameters.txt file for the channel (and set in the Castanet Publish).
getServerName() Returns the name of the server (transmitter) for this channel. The transmitter name is of the form hostname:port.
listChannelDirectory(String) Returns an array of strings representing the names of files in the given directory. To list the entire channel, use . as the argument. Returns null if the path does not exist.
listChannels() Returns an array of strings representing all the subscribed channels.
removeChannel(String) Remove this channel.
restart() Restart the channel.
startChannel(String, String) Start some other channel. The first argument is the same of the transmitter; the second is the name of the channel.
stop() Stop the channel.
subscribeChannel(String, String) Subscribe to a channel (don't start it). The arguments are the name of a transmitter and the name of the channel.
unsubscribeChannel(String) Unsubscribe from the named channel.
update() Update the channel.

Notes on the Channel Security Model

The security model for application channels is similar to that of applets. Application channels have their own security manager, which restricts the kinds of operations you can perform inside your channel. If you're used to working with applets, the security model is actually more relaxed; you now can save state to the user's local disk in specific directories. If you've been working primarily with stand-alone applications, however, be aware of the channel security restrictions:

Creating Channels That Use Bongo Presentations

Probably the easiest way to construct a channel from scratch is to create its interface in Bongo and then build the channel around that interface. Castanet provides both a class to do this–ApplicationPlayerFrame–and the infrastructure in the tuner to make linking together an interface and a Java channel program extremely easy–almost magically so, particularly if you're used to doing a lot of hand-coding in the AWT.

Even if you're used to working in the AWT, using a combination of Bongo and Java has several significant advantages over using AWT alone:

The disadvantage, of course, is that by using Bongo you must also include all the Bongo classes with your channel when you publish it, as you do with Bongo presentations. As with the presentations, however, the zip files are only downloaded once and shared between channels that use them.

Combining Java and Presentations

The following are the four steps to creating a channel that uses a Bongo presentation:

These steps are easier than they sound. Much of the linkage between the Java code and the presentation is automatic and managed by the tuner infrastructure and the application context. Let me go over the steps in more detail, and then I'll walk you through a simple application so that you can see the results.

Using the ApplicationPlayerFrame

As you learned earlier in this chapter, the ApplicationPlayerFrame class provides the basic channel structure you need to make your Java code interoperate with Castanet as a channel, including implementing the Application interface and storing the default application context in an instance variable called context.

In addition, the ApplicationPlayerFrame class also gives you access to the methods and utilities in a class called PlayerUtil. PlayerUtil is a Bongo-related class (from the marimba.gui package) with which you can gain access to various widgets inside the presentation. The ApplicationPlayerFrame class keeps track of the PlayerUtil object for the current presentation in the instance variable called util.

To use ApplicationPlayerFrame, simply create a Java class that extends it. Note that you need to import marimba.channel (for the application channel-related classes), marimba.gui (for the Bongo-related classes), and typically java.awt as well. Listing 10.2 shows a simple template.

Listing 10.2. A template for an ApplicationPlayerFrame class.

import java.awt.*;
import marimba.channel.*;
import marimba.gui.*;


public class MyChannelPresentation extends ApplicationPlayerFrame {
...
}

Note that you don't have to implement the Application interface; the ApplicationPlayerFrame class does this job for you. In fact, ApplicationPlayerFrame also includes basic versions of each of the four channel methods. You need to override them only if you need to do special things to initialize the application–for example, to read in saved state, to set up a network connection to the transmitter, or to create other windows that the application might use. In many simple cases, the only method you ever need to override is handleEvent() to process input from the users of your channel.

Linking Java and the Presentation Together

Note that nowhere in the preceding section did I mention opening the Bongo presentation file or otherwise creating an instance of a class to hold it, or using any methods to display the presentation onscreen. This was not an oversight. If you use ApplicationPlayerFrame, you don't have to do any of that work by hand in your channel class. Castanet and the ApplicationPlayerFrame class manage that process through the channel properties when you publish the channel. Figure 10.1 shows the general properties for an application channel that uses a presentation.

Figure 10.1. Application channel properties.



Note

The bongo.zip library is in the class path field for the reasons you learned in Chapter 9. You must include the bongo libraries with every channel that uses Bongo features. See the section titled "Putting It All Together: Publishing and Testing" for more.


If you simply include the name of the presentation in these properties, the ApplicationPlayerFrame class and the tuner open and display the presentation for you. Bongo also manages the display and updating of the screen. The only thing you have to concentrate on in your Java code is processing input from your users and interacting with the widgets in the presentation to produce a result. (I told you this process was easy.)

To interact with the widgets in the presentation, you intercept events from the presentation in your handleEvent() method. Bongo gets first crack at any events, so if built-in scripts are included in the presentation, these scripts can handle a lot of the simple events. But no scripts are included in the presentation, or if a particular event isn't covered by a script, you can intercept and handle that event in your Java code.

In addition to handling simple events, however, you also need a way to get values out of and put values back into the widgets in the presentation. You do so using that PlayerUtil object I mentioned earlier; it's stored in the util instance variable.

The PlayerUtil object provides a set of useful methods that you can use to get and set values and properties of various widgets. For example, if you have a text box widget named Password in your presentation, you can use the following line of Java code to extract the text from that text box widget:

String passwd = util.getText("Password");

To get ahold of a widget object to call more specific widget methods, you can use the getWidget() method and cast that widget object to the appropriate class:

SliderWidget slider = (SliderWidget)util.getWidget("volume");
slider.setValue(0); // mute the volume

Keep in mind that widget names are case sensitive. Make sure that the name you use in your Java code matches that in your presentation; otherwise, the connection between the two does not work.

In your Java code, you might also find the getName() method useful; it's defined on all widgets. In events in particular, you may have a reference to a widget (the target of an event) where you don't know the actual name of the widget. Using the getName() method and the equals() test, you can test to see whether the widget you have is indeed the one you want:

if ( ((Widget)evt.target).getName.equals("volume")) {

   // ... the event is coming from the widget named "volume."



}

Table 10.2 shows a few of the PlayerUtil methods that you may find useful in your Java code. You can call them via the util instance variable (for example, util.getValue()). Keep in mind that by getting a reference to the widget itself via getWidget(), you can call any method that any widget supports.



Note

Which methods do the widgets support? I don't have the space to go into all of them. The online API documentation for Bongo and The Official Marimba Guide to Bongo (Sams.net Publishing) have lots of information about these methods, as well as the complete set of methods defined in PlayerUtil.


Table 10.2. PlayerUtil methods.
Method name What it does
getText(String) Gets the text from the text box widget named in the argument. Returns a String object.
setText(String, String) Sets the text in the text box widget (named in the first argument) to the text in the second argument. Returns void.
clearText(String) Clears the text box named in the argument.
getValue(String) Gets the value of the widget named in the argument. Returns an Object. (Different widgets have different values; you usually need to covert the return value to something useful.)
setValue(String, Object) Sets the value of the widget named in the first argument to the Object in the second argument (usually a String or a Number object). Returns void.
getBoolean(String) Gets the value of the Checkbox or Option widget named in the argument. Returns true or false.
setBoolean(String, boolean) Sets the value of the Checkbox or Option widget named in the argument to the value of the second argument (true or false). Returns void.
getChoice(String) Gets the value of the Choice widget named in the argument. Returns a string.
setChoice(String,String) Sets the value of the Choice widget named in the first argument to the string in the second argument. Returns void.
show(String) Shows the widget named in the argument.
show(String, boolean) Shows or hides the argument named in the first argument. If the second argument is true, shows the widget; if false, hides the widget.
getWidget(String) Gets the widget object named by the argument. Returns a Widget object (which you most likely need to cast to a more specific widget class).


An Example: The Thermometer Channel

Let me show you a simple example to demonstrate how easy it is to create a channel that uses both a Bongo presentation and the ApplicationPlayerFrame class. This example is fairly simple. It doesn't do anything you couldn't do in Bongo alone, but it serves to demonstrate just how easy it is to put together a channel with ApplicationPlayerFrame.

The example in this section is a simple temperature converter to convert between Fahrenheit and Celsius temperatures and vice versa. The interface contains two spin box widgets for typing in the temperature directly (or incrementing it by one) and a slider for choosing the temperature via more direct means.

The Presentation

Figure 10.2 shows the presentation I created in Bongo for this example. Three widgets here are of note:

Figure 10.2. Thermometer presentation.

I saved this presentation as Thermometer.gui and stored it in my channel directory (which I called Thermometer as well).

The Java Code

Also inside my channel directory, I created a Java file called ThermometerApplication.java. The code for that application is shown in Listing 10.3.



Note

The code for this example, and for most of the examples in this book, is available on the CD-ROM that comes with the book. You don't have to retype it all in from here.


Listing 10.3. The ThermometerApplication code.

 1: import java.awt.*;

 2: import marimba.channel.*;

 3: import marimba.gui.*;

 4:

 5: public class ThermometerApplication extends ApplicationPlayerFrame {

 6:

 7:  public boolean handleEvent(Event evt) {

 8:

 9:    if ((evt.id == Event.ACTION_EVENT) && (evt.target instanceof Widget)) {

10:     Widget targ = (Widget)evt.target;

11:

12:     // redirect target to spinbox if user has changed text

13:     if (targ instanceof SpinBoxTextWidget)

14:       targ = ((SpinBoxTextWidget)targ).getOwner();

15:

16:     if (targ instanceof ValueWidget) {

17:        int temp = ((ValueWidget)targ).value;

18:       int newtemp = 0;

19:

20:       // user has changed value of fahrenheit spinbox

21:        if (targ.getName().equals("fahr")) {

22:          newtemp = (temp - 32) * 5 / 9;

23:          SpinBoxWidget dest = (SpinBoxWidget)util.getWidget("cels");

24:          dest.setValue(newtemp);

25:          util.setValue("slider",String.valueOf(temp));

26:          return true;

27:

28:       // user has changed value of celsius spinbox

29:        } else if (targ.getName().equals("cels")) {

30:          newtemp = temp * 9 / 5 + 32;

31:          util.setValue("fahr", String.valueOf(newtemp));

32:          util.setValue("slider",String.valueOf(newtemp));

33:          return true;

34:

35:       // user has changed  slider

36:        } else if (targ.getName().equals("slider")) {

37:          newtemp = (temp - 32) * 5 / 9;

38:          util.setValue("fahr",String.valueOf(temp));

39:          util.setValue("cels",String.valueOf(newtemp));

40:          return true;

41:      }

42:    }

43:  }

44:  return super.handleEvent(evt);

45:}

46:

47:}

Note the following about this example:

With the Java code written, all you have to do is compile it. Make sure that you have the bongo.zip and marimba.zip libraries from the Bongo installation available to your Java compiler or to your development environment.

Putting It All Together: Publishing and Testing

The final step is simply to publish the channel. The channel properties determine the link between the Java code and the presentation, so you cannot simply run the Java class and have it work. You have to run it from inside the Castanet framework.

Fire up Castanet Publish and add the development directory (called Thermometer) to the list of Channels Under Development. Then edit the properties for that channel. Next, switch to the General properties. Figure 10.3 shows the properties for the temperature converter channel.

Figure 10.3. Properties for the Temperature Converter channel.

The type of channel is Application; this is true whether or not your channel uses a presentation. As long as it implements the application interface, it's an application channel. The main class for the application is the one just created and the one that has the start() and stop() methods in it; here it's called ThermometerApplication (don't include the .class extension in the properties). And, finally, the presentation file that goes along with this channel is the Thermometer.gui file.

One other entry is important to note here: the file bongo.zip, which is included in the classpath field of the properties. This library should also be copied to your channel directory and published along with the channel. Whereas your users always have access to the channel classes through the tuner (the classes in marimba.zip), they might not have access to the bongo classes. If your channel uses any of the classes for Bongo presentations, including ApplicationPlayerFrame, you must publish the bongo.zip file along with your channel and include it here in the properties. This way, you can guarantee that your channel will work on your users' machines.



Note

Although you need to compile your files against the marimba.zip library, you don't need to include it with your channel. marimba.zip comes with every tuner; bongo.zip does not.


After you publish the channel, you can subscribe to it via a tuner and launch it just as you would any other channel. The tuner controls start up your main class, run the start() and setContext() methods, and open and display the presentation. If you've set up the properties correctly, you should now be able to interact with your channel.

If your channel runs into difficulties, you have to go back to your original code, make changes, republish the channel, and then update and relaunch it from the tuner to make sure that your changes work. See "Debugging Hints" for ideas on how to help with the debugging process for presentation-based or any kind of channel.

Using Multiple Presentations in a Channel

Setting up an application to use one presentation works exceptionally well when you have only one presentation. But what if your application uses multiple presentations, with different presentations appearing at a time?

The PlayerUtil class, which is available in the util instance variable, can help with this situation. The setPresentation() method allows you to open and set the current presentation for your application. However, inside a channel, you have to indicate the presentation file you want to open as a URL from the current channel directory. Here's a snippet of code to do just that:

try {

   util.setPresentation(new URL(context.getBase(), "mynewspresentation.gui"));

catch (MalformedURLException e) {

   e.printStacTrace();

}

Each time you work with URLs, you have to enclose your statements inside a try and catch block to catch any URL exceptions that might occur. The line here that contains the call to setPresentation() gets the current URL from the application context (using the getBase() method) and then appends the name of the presentation file to the end of that. (This example assumes that your GUI file is in the top level of the channel directory.)

If you want to pop up a new window with its own presentation and its own event management inside your application, then you have to worry about subclassing the class PlayerFrame. You use PlayerFrame to present Bongo presentations inside a channel window. It's the same class that allows the tuner to run a Bongo presentation as a channel without any other code. When you subclass the PlayerFrame class, be sure to create a constructor that takes an application context as an argument. And when you create the new PlayerFrame instance, pass in the current application context to that window so that you have access to it from that class. Also in that constructor, use the setPresentation() method to open the presentation you want to use for that window. Here's a simple example that opens a window for the presentation present.gui:

import java.awt.*;

import java.net.*;

import marimba.gui.*;

import marimba.channel.*;



public class NewFrame  extends PlayerFrame {

MainFrame parent;



public NewFrame (MainFrame parent, ApplicationContext context) {

    super();



    this.parent = parent;



    try {

      util.setPresentation(new URL(context.getBase(), "present.gui"));

   catch (MalformedURLException e) {

      e.printStackTrace();

   }

}



public boolean handleEvent(Event evt) {

   // deal with events from this presentation

}



{

Official Marimba Guide to Bongo, also published by Sams.net, contains examples of how to create subclasses of PlayerFrame and use them for your own presentations.

Converting Stand-alone Java Applications to Channels

If you do decide to stick with pure Java for your channels, you've already learned most of what you need to do to convert your existing framework to a channel-compatible framework. The following are the specific operations you need to do:

You'll learn about adding update and file-saving features to channels in the next chapter.

Converting Applets to Applications

Because of the special class that the tuner uses to run applets as applications (marimba.channel.AppletViewer), there's little you need to do to convert an applet to a channel, unless you want to go whole hog and port your applet code altogether (in which case, many of the same rules as for applications apply. Here are some things to keep in mind:

AppicationContext context =

((AppletContext)getAppletContext()).getApplicationContext();

You can then use that context and call its methods just as if the applet was a real channel. More about this in the next chapter.

Debugging Hints

Because the process of writing-compiling-publishing-updating-testing channels can be really slow, debugging becomes particularly important. It can keep you from spending all your time in the process and not enough in actual code. Unfortunately, few tools are currently available for debugging a channel before it's published. Until better tools are available, here are some suggestions for debugging channels:

Summary

Congratulations! After only 10 chapters, you've finally created your first real Castanet channel. In this chapter, you learned all about creating application channels, which are Java programs that are run from inside the tuner and can respond to updates and other tuner features.

The marimba.channel package contains the classes and interfaces you'll most likely use as your create your own channels: the Application interface; the ApplicationPlayerFrame class, which you can use to create application channels that use Bongo presentations; and the ApplicationFrame class for converting pure Java applications to channels. In addition, marimba.channel contains the definition of the application context (the ApplicationContext interface), which is used to interact with the tuner and the local channel directory.

With the basic framework for the channel in place, the next step is to add features specific to channels to that application. Read on to the next chapter for details!