| [ Team LiB ] |
|
20.1 Beans as JSP ComponentsJavaBeans components, or beans for short, are simply regular Java classes designed according to a set of guidelines. By following these guidelines, development tools can figure out how you intend the bean to be used and how it can be linked to other beans. The JavaBeans specification characterizes beans as classes that support:
Introspection means that information about a class, such as details about its methods and their parameters and return types, can be discovered by another class. By following certain naming conventions for the methods, the external class can figure out how the bean class is intended to be used. Specifically, the beans properties and the events it generates or observes can be found using the Java Introspection API. For GUI beans, a builder tool uses introspection to discover the bean's properties and present them to the user in a property window where they can be modified. In a JSP scenario, the JSP standard actions and the EL evaluator use introspection to find the methods for reading or writing property values and to declare variables of appropriate types. A property is an attribute of a bean that can be read or written by the bean's client through regular methods named according to the JavaBeans guidelines. Typically, the property value is represented by an instance variable in the bean, but a read-only property can also represent a value that's calculated at runtime. The property methods are used to customize the bean; for instance, you can set the label text for a bean used as a button in a GUI application or set the name of the data source for a faceless server-side bean. Besides property access methods, a bean class can have regular methods that perform actions such as saving the bean's properties in a database or sending a mail composed from its properties. A bean can generate or observe events. In a GUI bean, typical events are "button clicked" and "item selected." For a server-side bean, a typical event is "data source updated," allowing a bean that represents the data to refresh its copy. Support for persistence means that a bean should implement the java.io.Serializable interface. This interface flags a class that can be saved in an external format, such as a file. When tools customize a bean, it's possible to save the customized state during application development and then let the customized bean be instantiated at runtime. The <jsp:useBean> action allows you to take advantage of this feature, but it's not commonly used today because no JSP authoring tools provide a customization interface. There's another reason for supporting persistence in JSP beans, however. A servlet container can support session persistence, by saving all session data when a servlet context is shut down and reloading it again when the context is restarted. This works only if the beans you save in the session scope implement Serializable. In addition, beans (or any other object) placed in the session scope of an application marked as being distributable must be serializable, so that the container can migrate the session from one server to another. 20.1.1 JavaBeans Naming ConventionsAs I mentioned earlier, a Java bean is a class that has a no-argument constructor and conforms to the JavaBeans naming conventions. The bean properties are accessed through getter and setter methods, collectively known as a bean's accessor methods. Getter and setter method names are composed of the word get or set, respectively, plus the property name with the first character of each word capitalized. A regular getter method has no parameters but returns a value of the property's type, while a setter method has a single parameter of the property's type and has a void return type. Here's an example: public class CustomerBean implements java.io.Serializable {
String firstName;
String lastName;
int accountNumber;
int[] categories;
boolean preferred;
public String getFirstName( ) {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
A readable property has a getter method; a writable property has a setter method. Depending on the combination of getter and setter methods, a property is read-only, write-only, or read/write. Note that it's the presence of the accessor methods that defines the property; how the property value is represented inside the class makes no difference at all. A read-only property doesn't necessarily have to match an instance variable one-to-one. Instead, it can combine instance variable values, or any values, and return a computed value: public String getFullName( ) {
return (new StringBuffer(firstName).append(" ")
.append(lastName).toString( ));
}
The type of a property can be a Java class, interface, or a primitive type such as int: public int getAccountNumber( ) {
return accountNumber;
}
Besides simple single-value properties, beans can also have multivalue properties represented by an array of any type. This is called an indexed property in the specification. Two types of access methods can be used for an indexed property: methods reading or writing the whole array or methods working with just one element, specified by an index: public int[] getCategories( ) {
return categories;
}
public void setCategories(int[] categories) {
this.categories = categories;
}
public int getCategories(int i) {
return categories[i];
}
public void setCategories(int i, int category) {
this.categories[i] = category;
}
The naming convention for a Boolean property getter method is different from all other types. You can use the regular getter name pattern, but the recommendation is to use the word is combined with the property name, to form a question: public boolean isPreferred( ) {
return preferred;
}
This helps to make the source code more readable. The setter method for a Boolean property follows the regular pattern: public void setPreferred(boolean preferred) {
this.preferred = preferred;
}
Event handling is based on event observers implementing a listener interface, and event generators providing methods for observers to register their interest in the events. A listener interface defines the methods a listener needs to implement to be notified when the corresponding event is triggered. A bean identifies itself as a listener by declaring that it's implementing a listener interface, and an event source is identified by its listener registration methods. Let's look at an example. A listener interface for observing events related to the customer data handled by the example bean can look like this: import java.util.EventListener;
public interface CustomerUpdatedListener extends EventListener {
void customerUpdated(CustomerUpdatedEvent e);
}
The interface shown here defines only one event notification method, but an interface may also group a number of methods for related events. The CustomerBean identifies itself as an observer of the event by implementing the interface: public class CustomerBean implements CustomerUpdatedListener {
...
public void customerUpdated(CustomerUpdatedEvent e) {
if (e.getAccountNumber( ) == accountNumber) {
// Refresh local copy
}
}
Another bean, perhaps one acting as the gatekeeper to the customer database, identifies itself as a source for the event by defining methods for registration of listeners: import java.util.Vector;
public class CustomerRegister {
private Vector listeners = new Vector( );
public
void addCustomerUpdatedListener(CustomerUpdatedListener cul) {
listeners.addElement(cul);
}
public
void removeCustomerUpdatedListener(CustomerUpdatedListener cul) {
listeners.removeElement(cul);
}
public void updateCustomer(CustomerBean customer) {
// Update persistent customer storage
notifyUpdated(customer);
}
It notifies all listeners when the customer data is modified, like this: protected void notifyUpdated(CustomerBean customer) {
Vector l;
CustomerUpdatedEvent e =
new CustomerUpdatedEvent(this, customer.getAccountNumber( ));
synchronized(listeners) {
l = (Vector)listeners.clone( );
}
for (int i = 0; i < l.size( ); i++) {
((CustomerUpdatedListener)l.elementAt(i)).customerUpdated(e);
}
}
}
By following these simple naming conventions, the JSP standard actions <jsp:getProperty> and <jsp:setProperty>, as well as the EL evaluator and custom action classes, can discover how to use your beans correctly. At this time, no JSP features rely on the event-naming conventions, but future development tools may do so. So if your beans need to handle events, it's a good idea to follow the conventions. Besides, it's a well-known design pattern (you probably recognize it from the listener classes described in Chapter 19), so using it makes your code more readable to other developers familiar with this design. 20.1.1.1 Handling session eventsA bean used in a JSP application can actually register itself to receive session-related events. The Servlet API includes an interface called javax.servlet.http.HttpSessionBindingListener; an object that implements this interface is notified when it's placed in or removed from a session, through these two methods: public void valueBound(HttpSessionBindingEvent event); public void valueUnbound(HttpSessionBindingEvent event); The valueBound( ) method is called when the object is added to a session, and the valueUnbound( ) method is called when it's removed. The HttpSessionBindingEvent class contains these two methods: public String getName( ); public HttpSession getSession( ); The getName( ) method returns the name used for the object in the session, and the getSession( ) method returns a reference to the session object itself. This is different from the session attribute listener interface in the Servlet 2.3 specification—javax.servlet.http.HttpSessionAttributeListener, described briefly in Chapter 18. An object registered as an HttpSessionAttributeListener is notified when any session attribute is set, removed, or replaced, in all sessions; an object implementing the HttpSessionBindingListener interface is notified only when the object itself is added to or removed from the session it's placed in. The former is useful for application scope tasks, such as keeping track of the amount of memory used for session data; the latter can perform initialization and cleanup tasks for an individual object. 20.1.1.2 Using a package name for a bean classEven though the bean specification doesn't require it, I recommend that you always declare a specific package name for all beans you intend to use in JSP pages, via the Java package statement: package com.mycompany.beans;
public class MyBean {
...
}
If you don't, you can't use the bean in a JSP page in a portable manner. As you may recall from Chapter 16, the page implementation class may use a vendor-dependent package name. Because Java doesn't permit a class without a package qualifier to be used in a class that belongs to a package,[2] a JSP page containing beans that don't belong to a package cannot be compiled if the generated implementation class uses a package.
20.1.2 Compiling and Installing a BeanCompiling and installing a bean for a web application is done in the same way as for a servlet class, as described in Chapter 19. You need to include all classes the bean uses, if any, in the classpath and compile the bean, for instance using the javac command: C:/> set CLASSPATH=C:\someDir\someClasses.jar;%CLASSPATH% C:/> javac MyBean.java To make the bean available to the web application, place the resulting class file in the WEB-INF/classes directory for the example application: C:/> copy MyBean.class C:\Jakarta\jakarta-tomcat-5\webapps\ora\WEB-INF\classes If you followed my advice and declared a package name for the bean class, say com.mycompany.beans, you should put the class file in a directory under WEB-INF/classes that mirrors the package structure, for instance WEB-INF/classes/com/mycompany/beans. Alternatively, you can package the bean class file in a JAR file (see the Java SDK documents for details) and place the JAR file in the WEB-INF/lib directory. The internal structure of the JAR file must also mirror the package structure for all your classes. |
| [ Team LiB ] |
|