| ★ wanayoo — archive 1999 http://developer.iplanet.com/viewsource/fields_jspcomp/fields_jspcomp.html | Nouvelle recherche | Portail wanayoo |
![]() |
![]() |
![]() |
downloads | ||||||||||||||||
| technologies | |||||||||||||||||||
| support |
![]() |
![]() |
|
Building Your Own JSP Components
By Duane K. Fields and Mark Kolb
Send comments and questions about this article to View Source. Click here for printer-friendly version
[Editor's note: This article is adapted from a chapter of the book Web Development with JavaServer Pages, to be published in May 2000 by Manning Publications Co. It appears here with the permission of the publisher. More information about this book is available at Manning's web site.] The JavaServer Pages (JSP) component model is centered on Java software components called Beans, which must adhere to specifications outlined in the JavaBeans API. The JavaBeans API, created by Sun Microsystems with industry cooperation, dictates the rules that software developers must follow to create stand-alone, reusable Java software components. By using JSP's collection of Bean tags, content developers can use the power of Java to add dynamic elements to their pages without writing a single line of code. This article is written for developers who want to create their own Beans for use as JSP components, and for interested web designers who want to understand how these components are implemented behind the scenes. It is not necessary to understand the details of Beans development to work with JSP. As component architectures go, the interface between JSP and Beans is quite simple, as we will see. WHAT MAKES A BEAN A BEAN?So, what makes a Bean so special? You might be surprised to learn that a Bean is simply a Java class that follows a set of simple naming and design conventions outlined by the JavaBeans specification. Beans are not required to extend a specific base class or implement a particular interface. If a class follows the Bean conventions, and you treat it like a Bean, then it is a Bean. A particularly good thing about the Bean conventions is that they are rooted in sound programming practices that you may already be following to some extent. In the next section, we will discuss these conventions and show you how to create your own Beans.The JavaBeans APIFollowing the conventions specified by the JavaBeans API allows the JSP container to interact with Beans at a programmatic level, even though the container has no real understanding of what the Bean does or how it works. For JSP, we are primarily concerned with the aspects of the API that dictate the method signatures for a Bean's constructors and property access methods.Bean Are Just ObjectsAs with any other Java class, instances of Bean classes are simply Java objects. As a result, you always have the option of referencing Beans and their methods directly through Java code in other classes or through JSP scripting elements. Since they follow the Bean conventions, we can work with Beans without having to write Java code. Bean containers, such as a JSP container, can provide easy access to Beans and their properties. Following the JavaBeans API coding conventions, as we will see, means creating methods that control access to each property we wish to define for our Bean. Beans can also have regular methods like any other Java object. However, JSP developers will have to use scriptlets, expressions, or custom tags to access them, since a Bean container can manipulate a Bean only through its properties.Class Naming ConventionsYou might notice that in most examples Bean classes often include the word Bean in their name, such as UserBean, AlarmClockBean, DataAccessBean, and so forth. While this is a common approach and lets other developers immediately understand the intended role of the class, it is not a requirement in order for a Bean to be used inside a JSP page. Beans follow the same class naming rules as other Java classes: they must start with an alphabetic character, contain only alphanumeric and underscore characters, and be case-sensitive. Additionally, like other Java classes, it is common (but not required) to start the name of a Bean class with a capital letter.BEAN CONVENTIONSThe Bean conventions are what enable us to develop Beans, because they allow a Bean container to analyze a Java class file and interpret its methods as properties, designating the class as a Bean. The conventions dictate rules for defining a Bean's constructor and the methods that will define its properties.The Bean ConstructorThe first rule of JSP Bean building is that you must implement a constructor that takes no arguments. It is this constructor that the JSP container will use to instantiate your Bean through the <jsp:useBean> tag. Every Java class has a constructor method that is used to create instances of the class. If a class does not explicitly specify any constructors, a default zero-argument constructor is assumed. Because of this default constructor rule, the following Java class is perfectly valid and technically satisfies the Bean conventions:public class DoNothingBean { }
This Bean has no properties and cannot do or report anything useful, but
it is a Bean nonetheless. We can create new instances of it, reference
it from scriptlets, and control its scope. Here is a better example of
a class suitable for Bean usage; it has a zero-argument constructor
that records the time of its instantiation:
public class CurrentTimeBean {
private int hours;
private int minutes;
public CurrentTimeBean() {
java.util.Date now = new java.util.Date();
this.hours = now.getHours();
this.minutes = now.getMinutes();
}
}
In this example, we have used the constructor to initialize the Bean's instance variables
hours
and minutes to reflect the current time at instantiation. The
constructor of a Bean is the appropriate place to initialize instance variables
and prepare the instance of the class for use. Of course, in
order for it to be useful within a JSP page, we will need to define
some properties for the Bean and create the appropriate access methods
to control them.
The Magic of IntrospectionYou may be wondering how the JSP container can interact with Bean objects without the benefit of a common interface or base class to fall back on. Java manages this little miracle through a process called introspection that allows a class to expose its properties on request. The introspection process happens at run time, controlled by the Bean container.One way that introspection can occur is through a mechanism known as reflection, which allows the Bean container to examine any class at run time to determine its set of properties. The Bean container determines what properties a Bean supports by analyzing its public methods for the presence of property access methods that meet criteria defined by the JavaBeans API. For a property to exist, its Bean class must define an access method to return the value of the property, change the value of the property, or both. It is the presence alone of the specially named access methods that determines a Bean class's properties. Specifying a Bean's PropertiesAs we have mentioned, a Bean's properties are defined simply by creating appropriate access methods for them. Access methods are used to either retrieve a property's value or make changes to it. A method used to retrieve a property's value is called a getter, while a method that modifies its value is called a setter. Together these are generally referred to as access methods - they provide access to values stored in the Bean's properties.To define properties for a Bean, simply create a public method with the name of the property you wish to define, prefixed with the word get or set as appropriate. A getter method should return the appropriate data type, while the corresponding setter method should be declared void and accept one argument of the appropriate type. It is the get or set prefix that is Java's clue that you are defining a property. The signature for property access methods, then, is as follows:public void setPropertyName(PropertyType value); public PropertyType getPropertyName();For example, to define a property called rank, which can be used to store text and is both readable and writeable, we would need to create methods with these signatures: public void setRank(String rank); public String getRank();Likewise, to create a property called age that stores numbers: public void setAge(int age); public int getAge();
Let us revisit our previous example and make it more useful. We will add a couple of properties to our CurrentTimeBean, called hours and minutes, that will allow us to reference the current time in the page. For Java to recognize the existence of these properties, they will have to follow the getter method signatures defined by the JavaBeans design patterns. These methods should therefore look like this:public int getHours(); public int getMinutes();In our constructor, we store the current time's hours and minutes in instance variables. We can have our properties reference these variables and return their values where appropriate: public class CurrentTimeBean {
private int hours;
private int minutes;
public CurrentTimeBean() {
java.util.Date now = new java.util.Date();
this.hours = now.getHours();
this.minutes = now.getMinutes();
}
public int getHours() {
return hours;
}
public int getMinutes() {
return minutes();
}
}
That's all there is to it. The two methods simply return the appropriate
values as stored in the instance variables. Since these methods meet the
Bean conventions for naming access methods, we have just defined two properties
that we can access through JSP Bean tags. For example:
<jsp:useBean id="time" class="CurrentTimeBean"/> <HTML> <BODY> It is now <jsp:getProperty name="time" property="minutes"/>dieRoll and diceRoll properties are not managed by instance variables. Instead, we create a java.util.Random object in the constructor and call its random number generator from our access methods to dynamically generate property values. In fact, nowhere in the Bean are any actual static values stored for these properties - their values are recomputed each time the properties are requested. You are not required to create both getter and setter methods for each property you wish to provide for a Bean. If you wish to make a property read-only, define a getter method without providing a corresponding setter method. Conversely, creating only a setter method specifies a write-only property. The latter might be useful if the Bean uses the property value internally to affect other properties but is not a property you want clients accessing directly. Property Name ConventionsA common convention is that property names are mixed-case, beginning with a lowercase letter and uppercasing the first letter of each word in the property name. For the properties firstName and lastName, for example, the corresponding getter methods would be getFirstName() and getLastName(). Note the case difference between the property names and their access methods. Not to worry: the JSP container is smart enough to convert the first letter to uppercase when constructing the target getter method. If the first two or more letters of a property name are uppercased - for example, URL - the JSP container assumes that you really mean it, so its corresponding access methods would be getURL() and setURL().Indexed PropertiesBean properties are not limited to single values. Beans can also contain multivalued properties. For example, you might have a property named contacts that is used to store a list of objects of type Contact, containing phone and address information. Such a property would be used in conjunction with scriptlets or a custom iteration tag to step through the individual values. All values must be of the same type; however, a single indexed property cannot contain both string and integer elements, for example.To define an indexed valued property, you have two options: create an access method that returns the entire set of properties as a single array, or access elements of the set by using an index value. In creating an access method that returns the entire set of properties as a single array, a JSP page author or iterative custom tag can determine the size of the set and iterate through it. For example: public PropertyType[] getProperty()Accessing elements of the set by using an index value allows additional flexibility. For example, you might want to access only particular contacts from the collection: public PropertyType getProperty(int index)While not specifically required by the Bean conventions, we find it useful to implement both method styles for a multivalued property. It is not much more work and it gives you a good deal more flexibility in using the Bean. To set multivalued properties, there are setter method signatures analogous to the getter method naming styles described earlier. The syntax for these methods is as follows: public void setProperty(int index, PropertyType value) public void setProperty(PropertyType[])Another type of method that is commonly implemented and recognized by Bean containers is the size() method, which can be used to determine the size of an indexed property. A typical implementation might be public int getPropertySize()This is yet another method that is not required but increases the flexibility of the design and gives page developers more options to work with. Example: A Bean With Indexed PropertiesIn this example, we will build a component that can perform statistical calculations on a series of numbers. The numbers themselves are stored in a single, indexed property. Other properties of the Bean hold the value of statistical calculations, like the average or the sum.package com.manning.jsp;
import java.util.*;
public class StatBean {
private double[] numbers;
public StatBean() {
numbers = new double[0];
}
public double getAverage() {
double sum = this.getSum();
if (sum == 0)
return 0;
else
return sum/numbers.length;
}
public double getSum() {
double sum = 0;
for (int i=0; i < numbers.length; i++)
sum += numbers[i];
return sum;
}
public double[] getNumbers() {
return numbers;
}
public double getNumbers(int index) {
return numbers[index];
}
public void setNumbers(double[] numbers) {
this.numbers = numbers;
}
public void setNumbers(int index, double value) {
numbers[index] = value;
}
public int getNumbersSize() {
return numbers.length;
}
}
Since the JSP Bean tags deal exclusively with scalar properties, the only
way to interact with indexed properties such as these is through JSP scriptlets
and expressions. In this JSP page we will use a JSP scriptlet in the body
of the <jsp:useBean> tag to pass an array of integers to the
Bean's numbers property. We will have to use a scriptlet to display
back the numbers themselves, but we can use a <jsp:getProperty>
tag to display the average.
<jsp:useBean id="stat" class="com.manning.jsp.StatBean">
<%
double[] mynums = {100, 250, 150, 50, 450};
stat.setNumbers(mynums);
%>
</jsp:useBean>
<HTML>
<BODY>
The average of
<%
double[] numbers = stat.getNumbers();
for (int i=0; i < numbers.length; i++) {
if (i != numbers.length)
out.print(numbers[i] + ",");
else
out.println("" + numbers[i]);
}
%>
is equal to <jsp:getProperty name="stat" property="average"
/>
</BODY>
</HTML>
The use of custom tags, a technique that we will discuss in the book, can
greatly aid in working with indexed properties, eliminating the need
for inline code by encapsulating common functionality into simple tag
elements.
Accessing Indexed Values Through JSP Bean TagsWe might also want to include a method that will enable us to pass in the array of numbers through a Bean tag. Since Bean tags deal exclusively with single values, we will have to perform the conversion ourselves. We will create a pair of access methods that treat the array as a list of numbers stored in a comma-delimited string. To differentiate between these two approaches, we will map the String versions of our new access methods to a new property we will call numbersList. Note that even though we are using a different property name, it is still modifying the same internal data and will cause changes in the average and numbers properties.public void setNumbersList(String values) {
Vector n = new Vector();
StringTokenizer tok = new StringTokenizer(values, ",");
while (tok.hasMoreTokens())
n.addElement(tok.nextToken());
numbers = new double[n.size()];
for (int i=0; i < numbers.length; i++)
numbers[i] = Double.parseDouble((String)
Now we can access this Bean through JSP tags alone:
<jsp:useBean id="stat" class="com.manning.jsp.StatBean"> <jsp:setProperty name="stat" property="numbersList"
Boolean PropertiesFor boolean properties, which hold only true or false values, you can elect to use another Bean convention for getter methods. This convention is to prefix the property name with is and return a boolean result. For example, consider these method signatures:public boolean isProperty(); public boolean isEnabled(); public boolean isAuthorized();The container will automatically look for this form of method if it cannot find a property access method matching the getter syntax discussed earlier. You can set the value of a boolean property with the same style setter methods you would use for other properties, as shown below. public void setProperty(boolean b); public void setEnabled(boolean b); public void setAuthorized(boolean b); JSP Type ConversionA JSP component's properties are not limited to string values, but it is important to understand that all property values accessed through the <jsp:getProperty> tag will be converted to strings. A getter method need not return a String explicitly, however, as the JSP container will automatically convert the return value to a String as needed. For the Java primitive types, conversion is handled by the methods shown in Table 1.
Similarly, all property setter methods accessed with a <jsp:setProperty>
tag will be automatically converted from a String to the appropriate
native type by the JSP container. This is accomplished via methods of Java's
wrapper classes, as shown in Table 2.
Properties are not restricted to primitive types, either. For objects,
the JSP container will invoke the object's toString() method,
which, unless you have overloaded it, will probably not be very representative
of the data stored in the object. For properties representing objects too
complex to represent with a String or native Java type, you have
several strategies. You can create getters and setters that accept a String
or native type and then perform the necessary conversions for creating
an appropriately typed object from the String or native data.
You can also overload your getter and setter methods to accept the appropriate
object type, although custom tags or JSP scripting elements will be required
to access the overloaded methods, since the <jsp:setProperty>
and <jsp:getProperty> tags work exclusively with String
values. You can also set the property indirectly - for example,
allowing the user to set the hours and minutes separately through a pair
of write-only properties and having a single read-only property called
time.
Even though the Bean tags do not allow you to pass any arguments into
a Bean's constructor, you can still define constructors that take arguments.
You will not, however, be able to call them through Bean tags. The only
way to instantiate an object requiring arguments in its constructor within
a JSP page is through a scriptlet. You will be able to access the Bean's
properties through the normal FV = principal(1 + interest rate/compounding periods) ^ (years * compounding
periods)
This Bean will require the following information:
Since the user will probably want to display the input values in addition
to configuring them, we have given the user both read and write access.
The futureValue property is designated read-only because it will
reflect the results of the calculation. To determine
the value of the futureValue property, the JSP container plugs
the values of the other properties into our interest formula. (If
you wanted to get fancy, you could write a Bean that, given any four of
the properties, could calculate the remaining property value.) We will
store our initialization properties in instance variables:
We chose to initialize our properties with legal values to keep our
Bean in a legal state. Of course, this might not be appropriate in every
situation. For Beans particularly sensitive to their configuration state,
you might need to design a scheme for marking a property as uninitialized,
such as setting it to To create a There is also a One area where the Some servers support indefinite long-term session persistence by writing
any session data (including Beans) to disk between server shutdowns. When
the server comes back up, the serialized data is restored. This same reasoning
applies to servers that support clustering in heavy traffic environments.
Many of them use serialization to replicate session data among a group
of web servers. If your Beans do not implement the Using a similar tactic, you might choose to store serialized copies
of your Beans to disk, an LDAP server, or a database for later use. You
could, for example, implement a user's shopping cart as a Bean, which you
store in the database between visits.
If a Bean requires particularly complicated configuration or setup,
it may be useful to fully configure the Bean's properties
as required and then serialize the configured Bean to disk. This "snapshot"
of a Bean can then be used anywhere you would normally be required to create
and configure the Bean by hand, including the The The Each of these events is associated with an By building its component model around the relatively simple JavaBeans
API, JSP enables Java developers to quickly package a web application's
core functionality into discrete, reusable software components. These components
can then be incorporated into JSP pages as easily as HTML elements. This
approach allows for a cleaner division of labor between application and
content developers.
Write to us and let us know what you think of this article. Duane Fields is a Senior Engineer for the E-Business Enablement group of IBM's Tivoli Systems, where he creates web-based applications with Java and JSP. He lives in Austin, Texas. Mark Kolb has a Ph.D. in aerospace engineering and was the recipient of a NASA Space Act Award. He now leads the development of server-side web applications using servlets and JSP in Tivoli Systems' Internet Business Unit. He lives in Round Rock, Texas. (11.99)
Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use |