Java 1.2 Unleashed

Previous chapterNext chapterContents


- 29 -

Glasgow Developments


The capabilities of JDK 1.2 JavaBeans have been significantly enhanced since JDK 1.1. These enhancements are the result of an upgrade program, referred to as Glasgow. The Glasgow Specification (http://java.sun.com/beans/glasgow/) documents the objectives, rationale, requirements, and design of this upgrade. The JavaBeans improvements resulting from Glasgow can be grouped into the following three functional areas:

Each of these three areas greatly enhances the capabilities of JavaBeans. The Extensible Runtime Containment and Services Protocol allows beans to be constructed in a hierarchical fashion, with bean containers providing direct services to their bean components. The JAF allows beans to be activated during execution time to process data of a variety of types. The Drag and Drop API allows JavaBeans to provide the same GUI capabilities as other component frameworks.

In this chapter, you'll focus on the first two Glasgow developments--Chapter 16, "Working with Drag and Drop," is dedicated to the third. You'll learn about the cap- abilities each area provides, and how to use them in the beans you develop. When you finish this chapter, you'll be familiar with the most recent developments in JavaBeans technology.

The Extensible Runtime Containment and Services Protocol

As described in Chapter 24, "The Software Component Assembly Model," the overall objective of component-based software development is to develop software components that can be used to assemble software on a component-by-component basis. In this model, existing components are used to assemble new components, which may be used to develop even more complex components. These components are then assembled into applets or applications via visual programming tools.

The JavaBeans implementation provided with JDK 1.1 allowed beans to be assembled into component hierarchies, with parent beans containing one or more child beans. However, the JDK 1.1 JavaBeans implementation did not provide any facilities for child beans to learn about their parent containers or the services they provide. As a result, child beans were not able to interact with their parents (or siblings) or make use of their (family) environment. For example, suppose that you want to use a multimedia bean as a container for part of an application, and you want to add custom bean controls to the multimedia bean. The bean controls have no way of obtaining information about their container or the multimedia services it provides.

The Extensible Runtime Containment and Services Protocol solves the lack of communication between child beans and their parent containers. This protocol adds the following capabilities to JDK 1.1 JavaBeans:

These capabilities are provided through the java.beans.beancontext package, which is introduced in Chapter 26, "Developing Beans." This package provides classes and interfaces for enabling beans to access their execution environment, referred to as their bean context. The BeanContextChild and BeanContext interfaces implement this concept.

The BeanContextChild interface provides methods for getting and setting this context, and for managing context-related event listeners. All context-aware beans must implement this interface.

BeanContextChild is extended by the BeanContext interface, which provides methods by which beans can access resources and services that are available within their context. Objects that implement BeanContext function as containers for other beans (which implement BeanChildContext). When a child bean that implements BeanChildContext is added to a parent bean container that implements BeanContext, the parent invokes the setBeanContext() method of its child. When the child wants to access its environment (BeanContext), it invokes its getBeanContext() method.

A bean can access its environment through its BeanContext. A BeanContext may or may not expose its services to the beans that it contains. The BeanContextServicesSupport interface extends the BeanContext interface to provide child beans with access to the services of their BeanContext. A bean that provides services to its children must implement this interface. The getCurrentServiceClasses(), hasServices(), and getService() methods are invoked by the child beans to access these services. The BeanContextServiceProvider interface is implemented by objects that provide instances of a particular service. The BeanContextContainer interface is implemented by BeanContext objects that are associated with AWT containers.


NOTE: Because a bean's BeanContext is subject to change, a bean's references to its BeanContext must be declared as transient.

The BeanContextChildSupport class provides a default implementation of the BeanContextChild interface.

The BeanContextSupport class extends BeanContextChildSupport to provide an implementation of the BeanContext interface. This class provides variables and methods for managing the beans that are contained in the context. It defines two inner classes: BeanContextSupport.BCSChild and BeanContextSupport.BCSIterator. These classes are used to maintain information on the beans that are contained within a bean context.

The BeanContextServicesSupport class extends BeanContextSupport and imple- ments the BeanContextServices interface. It defines two inner classes: BeanContextServicesSupport.BCSSChild and BeanContextServicesSupport. BCSSServiceProvider. BeanContextServicesSupport.BCSSChild extends BeanContextSupport.BCSChild, and BeanContextServicesSupport. BCSSServiceProvider is used to access BeanContextServiceProvider objects.

BeanContext Event Handling

The Extensible Runtime Containment and Services Protocol allows a beans's BeanContext to change during a program's execution. It provides events and event handling interfaces to deal with changes in a bean's BeanContext. The BeanContextEvent class is the superclass of all BeanContext-related events. It is extended by BeanContextMembershipEvent, BeanContextServiceAvailableEvent, and BeanContextServiceRevokedEvent. The BeanContextMembershipEvent class defines events that occur as the result of changes in a bean's membership in a BeanContext. It is extended by BeanContextAddedEvent and BeanContextRemovedEvent. The BeanContextServiceAvailableEvent reports changes in the availability of services, and BeanContextServiceRevokedEvent reports the revocation of services.

The BeanContextMemberShipListener interface is used to handle the BeanContextMembershipEvent event. The BeanContextServicesListener interface is used to handle the BeanContextServiceAvailableEvent event. The BeanContextServiceRevokedListener interface is used to handle the BeanContextServiceRevokedEvent event.

A BeanContext Example

The BeanContextApp application, shown in Listing 29.1, illustrates the use of the java.beans.beancontext package. The program's output is nothing special (see Figure 29.1), but what's happening under the hood is quite interesting.

FIGURE 29.1. The BeanContextApp display.

The text displayed in the text area is generated by a bean (an object of the ChildBean class) that is contained in another bean (an object of the ParentBean class). Listing 29.2 contains the source code of the ChildBean class, and Listing 29.3 contains the source code of the ParentBean class. The child bean obtains access to its BeanContext and queries the context about the services it provides. One of these services is the TextArea that is displayed in the program's frame window. The parent bean passes a reference to the TextArea object to the child, and the child uses it to display its output.

The JAFApp program is a simple component that displays a TextArea object. Its only processing of note is in the setup() method. After adding the TextArea object to the center of its frame, it creates a ParentBean object, passing a reference to the TextArea object in the ParentBean constructor. It then creates an object of the ChildBean class and adds it to the ParentBean object. Finally, it invokes the useContext() method of the ChildBean object.

The ChildBean class shows how to use a BeanContext to access services that are provided by a parent bean. The ChildBean class extends the BeanContextChildSupport class and implements the BeanContextServiceRevokedListener interface. It creates a TextArea object of its own, which it uses to pass parameters of the Class class to its parent.

The useContext() method invokes the getBeanContext() method to retrieve the bean's BeanContext and cast it into a BeanContextServices object. This object is used to determine whether the bean context provides a service of the TextArea class. The getService() method is used to obtain the object implementing this service (from the bean context). The TextArea object that is returned is then used to display the text message to the application window.

The serviceRevoked() method is a stub that is used to implement the BeanContextServiceRevokedListener interface. An object of this interface is required as an argument to the getService() method.

The ParentBean class provides the bean context of the ChildBean object. It extends the BeanContextServicesSupport class and implements the BeanContextServiceProvider interface. Its constructor receives and stores a TextArea object, and then adds a service related to this object.

Two getService() methods are provided. One overrides the method of the BeanContextServicesSupport class, and the other is used to implement the BeanContextServiceProvider interface. Both return the TextArea object.

The getCurrentServiceSelectors() and releaseService() methods are also used to implement the BeanContextServiceProvider interface.

LISTING 29.1. THE BeanContextApp PROGRAM.

import java.awt.*;

import java.awt.event.*;

import ju.ch09.*;

public class BeanContextApp extends Frame {

 Object menuItems[][] = {{"File","Exit"}};

 MenuItemHandler mih = new MenuItemHandler();

 MyMenuBar menuBar = new MyMenuBar(menuItems,mih,mih);

 int screenWidth = 400;

 int screenHeight = 400;

 TextArea textArea = new TextArea();

 public static void main(String args[]){

  BeanContextApp app = new BeanContextApp();

 }

 public BeanContextApp() {

  super("BeanContextApp");

  setMenuBar(menuBar);

  setup();

  setSize(screenWidth,screenHeight);

  addWindowListener(new WindowEventHandler());

  show();

 }

 void setup() {

  add("Center",textArea);

  ParentBean parent = new ParentBean(textArea);

  ChildBean child = new ChildBean();

  parent.add(child);

  child.useContext();

 }

 class MenuItemHandler implements ActionListener, ItemListener {

  public void actionPerformed(ActionEvent ev){

   String s=ev.getActionCommand();

   if(s.equals("Exit")){

    System.exit(0);

   }

  }

  public void itemStateChanged(ItemEvent e){

  }

 }

 class WindowEventHandler extends WindowAdapter {

  public void windowClosing(WindowEvent e){

   System.exit(0);

  }

 }

}

LISTING 29.2. THE ChildBean BEAN.

import java.awt.*; 

import java.beans.*;

import java.beans.beancontext.*;

 

public class ChildBean extends BeanContextChildSupport 

  implements BeanContextServiceRevokedListener {

  

 TextArea textArea = new TextArea();

 

 public void useContext() {

  BeanContextServices beanContext =

   (BeanContextServices) getBeanContext();

  if(beanContext.hasService(textArea.getClass())) {

   try {

    textArea = (TextArea) beanContext.getService(this, this, 

     textArea.getClass(), null, this);

   }catch(Exception ex){

    System.out.println(ex.toString());

   }

   String msg = "The child was able to access the services of its parent.";

   textArea.setText(msg);

  }

 }

 

 public void serviceRevoked(BeanContextServiceRevokedEvent ev) {

 }

}

LISTING 29.3. THE ParentBean BEAN.

import java.awt.*; 

import java.beans.*;

import java.util.*;

import java.beans.beancontext.*;

 

public class ParentBean extends BeanContextServicesSupport 

  implements BeanContextServiceProvider {

  

 TextArea textArea;

 

 public ParentBean(TextArea textArea) {

  this.textArea = textArea;

  addService(textArea.getClass(), this);

 }

 

 public Object getService(BeanContextChild child,

   Object requestor, Class serviceClass, Object serviceSelector,

   BeanContextServiceRevokedListener bcsrl) 

   throws TooManyListenersException {

  return textArea;

 }

 public Object getService(BeanContextServices bcs,

   Object requestor, Class serviceClass, Object serviceSelector) {

  return textArea;

 }

 public Iterator getCurrentServiceSelectors(BeanContextServices bcs,

   Class serviceClass) {

   return null;

 }

 

 public void releaseService(BeanContextServices bcs,

   Object requestor, Object service) {

 }

}

The JavaBeans Activation Framework

In many applications, software is called upon to process data of arbitrary types. The application is required to determine the type of data that it is to process, determine which operations can be performed on the data, and instantiate software components for performing those operations. For example, consider a Web browser that displays data loaded from a URL. Some URLs reference HTML files, some reference image files, and others may reference files containing scripting data. The Web browser determines the type of data contained in the file using MIME type information provided by Web servers. It then selects external or internal components that display the file's data, launches instances of those components, and feeds the file data to those components for display.

The JavaBeans Activation Framework is used to support this type of data processing by associating beans with the types of data that they support. It provides the following capabilities:

The JAF API is implemented by the javax.activation package, which is a standard API extension. This package consists of the following classes and interfaces:

Before the JAF can be used, a CommandMap must be created that maps MIME types, and operations on those types, to bean classes. This is typically accomplished using the MailcapCommandMap class. External mailcap files can be used to set up the mapping, or it can be set up during program initialization. Once the CommandMap has been created, the JAF is ready for use.

The JAF is used by creating a DataSource for data that is to be processed. This data is typically stored in a file or referenced by a URL. A DataHandler is then constructed from the DataSource. The getContentType() method of DataHandler is used to retrieve the MIME type associated with the DataSource. This MIME type is used to retrieve a CommandInfo array from the CommandMap. The CommandInfo array presents the list of operations that are supported by the DataSource. A selected CommandInfo object is passed to the getBean() method of DataHandler to create an instance of a bean that supports a specific operation on a MIME type. The bean is added to the applet or application's GUI, and the bean's setCommandContext() method is invoked to cause the bean to perform the desired operation on the data contained in the DataSource.

JAF-compliant beans are required to implement the CommandObject interface. This interface consists of the single setCommandContext() method.

The JAFApp program in the next section illustrates the use of the interfaces and classes described in the previous paragraphs.


NOTE: Make sure that you download and install the JAF before going to the next section. It can be downloaded from JavaSoft's Web site at http://java.sun.com/beans/glasgow/jaf.html. To use the JAF, the activation.jar file must be placed in your CLASSPATH.

The JAFApp Program

The JAFApp program in Listing 29.4 shows how the JAF is used to read a file, determine its MIME type, and instantiate a bean to display the file's contents. The program's opening display is shown in Figure 29.2. It is a bare frame containing no GUI components. Use the Open menu item in the File menu to open the test.txt file that is provided in the ch29 directory. A bean is created to display the file's contents, as shown in Figure 29.3. Now open the test.gif file that is located in the same directory. The old bean is removed and a new bean is instantiated to display the GIF file, as shown in Figure 29.4.


NOTE: Make sure that the ju.ch29 package is in your CLASSPATH before you run the JAFApp program.

FIGURE 29.2. The JAFApp opening display.

FIGURE 29.3. The TextDisplay bean displays the text file.

FIGURE 29.4. The ImageDisplay bean displays the GIF file.

The JAFApp class declares a variable of the MailcapCommandMap class and assigns it to the mailcap variable. This variable is initialized in the setup() method. The bean variable is declared as an object that supports the CommandObject interface. This object is assigned to beans that are dynamically instantiated by the JAF.

The setup() method specifies that an object of the ju.ch29.TextDisplay class should be used to implement the view operation on data of the text/plain MIME type. It also specifies that an object of the ju.ch29.ImageDisplay class should be used to implement the view operation on data of the image/gif MIME type. This mapping is essential to specifying the behavior of the JAF.

The displayFile() method is invoked to display a file using the JAF. It creates a FileDataSource object from the name of the file to be displayed, and then creates a DataHandler object from the FileDataSource object. The getContentType() method returns the MIME type of the data source. This MIME type is used to retrieve an array of CommandInfo objects that specify the commands that are supported on this MIME type. The array is searched for an object that implements the view command, and a bean implementing the CommandObject interface is created and instantiated via the getBean() method of the DataHandler interface. The CommandInfo object associated with the view command is passed as an argument to getBean().

The bean is cast to a Component and added to the program's GUI. The validate() method is then invoked to lay out the window. Finally, the bean is brought to life by invoking its setCommandContext() method, passing a reference to the DataHandler interface. This causes the bean to display the data source in its GUI component's display area.

The TextDisplay class, shown in Listing 29.5, extends TextArea and implements the CommandObject interface. This interface consists of the single setCommandContext() method. This method obtains an InputStream object from the DataHandler, reads the data contained in the stream, converts it to a String object, and displays it in its text area.

The ImageDisplay class (see Listing 29.6) is similar to TextDisplay. However, ImageDisplay extends a Canvas object instead of a TextArea object. Its setCommandContext() method reads the data from the InputStream object provided by the DataHandler and converts it to an Image object. The repaint() method is invoked to cause the Image object to be painted to the Graphics object associated with the Canvas.

LISTING 29.4. THE JAFApp PROGRAM.

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import javax.activation.*;

import ju.ch09.*;

import ju.ch29.TextDisplay;

import ju.ch29.ImageDisplay;

public class JAFApp extends Frame {

 Object menuItems[][] = {{"File","Open","-","Exit"}};

 MenuItemHandler mih = new MenuItemHandler();

 MyMenuBar menuBar = new MyMenuBar(menuItems,mih,mih);

 int screenWidth = 400;

 int screenHeight = 400;

 String directory = ".";

 MailcapCommandMap mailcap = new MailcapCommandMap();

 CommandObject bean = null;

 

 public static void main(String args[]){

  JAFApp app = new JAFApp();

 }

 public JAFApp() {

  super("JAFApp");

  setMenuBar(menuBar);

  setup();

  setSize(screenWidth,screenHeight);

  addWindowListener(new WindowEventHandler());

  show();

 }

 void setup() {

  mailcap.addMailcap("text/plain; ; x-java-view=ju.ch29.TextDisplay");

  mailcap.addMailcap("image/gif; ; x-java-view=ju.ch29.ImageDisplay");

 }

 void displayFile(String fileName) {

  FileDataSource fds = new FileDataSource(fileName);

  DataHandler dh = new DataHandler(fds);

  String mimeType = dh.getContentType();

  try {

   CommandInfo[] commands = mailcap.getPreferredCommands(mimeType);

   for(int i=0;i<commands.length;++i) {

    if(commands[i].getCommandName().equals("view")) {

     if(bean!=null) remove((Component) bean);

     bean = (CommandObject) dh.getBean(commands[i]);

     add("Center",(Component) bean);

     validate();

     bean.setCommandContext("",dh);

    }

   }

  }catch(Exception ex){

   System.out.println(ex.toString());

  }

 }

 String getFileName() {

  // Display file dialog

  FileDialog dialog = new FileDialog(JAFApp.this,

  "Open File", FileDialog.LOAD);

  dialog.setDirectory(directory);

  dialog.show();

  if(dialog.getFile()==null) return null;

  directory = dialog.getDirectory();

  String file = directory + dialog.getFile();

  return file; 

 }

 class MenuItemHandler implements ActionListener, ItemListener {

  public void actionPerformed(ActionEvent ev){

   String s=ev.getActionCommand();

   if(s.equals("Exit")){

    System.exit(0);

   }else if(s=="Open"){

    // Get the name of the file

    String fileName = getFileName();

    if(fileName == null) return;

    displayFile(fileName);

   }

  }

  public void itemStateChanged(ItemEvent e){

  }

 }

 class WindowEventHandler extends WindowAdapter {

  public void windowClosing(WindowEvent e){

   System.exit(0);

  }

 }

}

LISTING 29.5. THE TextDisplay BEAN.

package ju.ch29;

import java.awt.*; 

import java.io.*; 

import javax.activation.*;

 

public class TextDisplay extends TextArea

  implements Serializable, CommandObject {

 

 public void setCommandContext(String verb, DataHandler dh) 

   throws IOException {

  if(dh!=null) {

   InputStream inStream = dh.getInputStream();

   byte[] bytes = new byte[inStream.available()];

   inStream.read(bytes);

   setText(new String(bytes));

  }

 }

}

LISTING 29.6. THE ImageDisplay BEAN.

package ju.ch29;

import java.awt.*; 

import java.io.*; 

import javax.activation.*;

 

public class ImageDisplay extends Canvas

  implements Serializable, CommandObject {

 public static final int WIDTH = 200;

 public static final int HEIGHT = 200;

 Image image = null;

 

 public ImageDisplay() {

  super();

 }

 public Dimension getPreferredSize() {

  return new Dimension(WIDTH,HEIGHT);

 }

 public Image getImage() {

  return image;

 }

 public void setImage(Image image) {

  this.image = image;

 }

 public synchronized void paint(Graphics g) {

  if(image!=null) g.drawImage(image,0,0,this);

 }

 public void setCommandContext(String verb, DataHandler dh) 

   throws IOException {

  if(dh!=null) {

   InputStream inStream = dh.getInputStream();

   byte[] bytes = new byte[inStream.available()];

   inStream.read(bytes);

   image = Toolkit.getDefaultToolkit().createImage(bytes);  

   repaint();

  }

 }

}

Summary

In this chapter, you learned about the Glasgow developments and how to use the capabilities they provide in the beans you develop. You learned about the Extensible Runtime Containment and Services Protocol and how it is used to create bean hierarchies. You also learned how the JAF is used to select beans based upon the MIME types they support. In the next chapter, you'll change course and learn about the network programming capabilities provided by JDK 1.2.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.