>
(See.
ESIS Example: Input document
>
(See.
6
ESIS example: ASCII output of nsgmls (ARTICLE (PARA -Written by the lecturer. AREFID TOKEN FIG1 (FIG(FIG -REF )FIG)FIG -REF -.)
\ n(See.
ESIS example: ASCII output of nsgmls (ARTICLE (PARA -Written by the lecturer. \ n(See. AREFID TOKEN FIG1 (FIG(FIG -REF )FIG)FIG -REF -.) AID TOKEN FIG1 AFILE CDATA pekka.jpg ACAPTION CDATA The Lecturer (FIG )FIG )PARA )ARTICLE C 7
Command--line parser interface Command Application Ai CDATA 1
line call
Stream
-Hi!
Command--line parser interface Command Application Ai CDATA 1
line call
Stream
-Hi! )A
SGML/XML Parser > Hi! 8
Event Call Call--Back Interfaces
Application implements a set of callback methods for handling parse events – parser notifies the application by method calls – » parameters qualify events further (element type name, names and values of attributes, values of content strings,
Event Call Call--Back Interfaces
Application implements a set of callback methods for handling parse events – parser notifies the application by method calls – » parameters qualify events further (element type name, names and values of attributes, values of content strings, …)
Idea behind ‘‘SAX’’ (Simple API for XML) – an industry standard API for XML parsers –
9
An event callback application Application Main Routine C a l
s ar
ocumen "A",[i="1"]
Parse()
An event callback application Application Main Routine C a l l b a c k R o u t i n e s
s ar
Parse()
ocumen "A",[i="1"]
startElement() "Hi!"
characters() endElement()
"A"
Hi! 10
Object Model Interfaces
Application interacts with an objectobject -oriented representation of – the parser – – the document parse tree consisting of objects like – document element, attribute, text
Object Model Interfaces
Application interacts with an objectobject -oriented representation of – the parser – – the document parse tree consisting of objects like – document , element, attribute, text , …
Abstraction level higher than in event based interfaces; more powerful access (e.g. to descendants, following siblings) Drawback: Higher memory consumption 11
An ObjectObject-Model Based Application Application Parse
Build/ Load/ Modify
In--Memory Document In Representation Document
An ObjectObject-Model Based Application Application
Build/ Load/ Modify
Parse Parser Object
In--Memory Document In Representation Document A
Build
i=1
"Hi!" Hi! 12
DOM and SAX The Simple API for XML (SAX) is an alternative to avoid problem with DTDs Since Java has been commonly used for processing XML files, this section presents one popular Java-based method of parsing XML: the Document Object Model (DOM). DOM represents an entire XML document in a tree-like data structure that can be easily
manipulated by a Java program. The advantage of DOM is that it is relatively simple to use and you can modify the data structure in addition to extracting data from it. However, the
disadvantage is that DOM always parses and stores the entire document, even if you only care
DOM and SAX The Simple API for XML (SAX) is an alternative to avoid problem with DTDs Since Java has been commonly used for processing XML files, this section presents one popular Java-based method of parsing XML: the Document Object Model (DOM). DOM represents an entire XML document in a tree-like data structure that can be easily
manipulated by a Java program. The advantage of DOM is that it is relatively simple to use and you can modify the data structure in addition to extracting data from it. However, the
disadvantage is that DOM always parses and stores the entire document, even if you only care about part of it.
To use DOM, you need have a DOM-compliant parser. A list of such parsers are given at http://www.xml.com/pub/rg/Java_Parsers.
Besides, the Java API for XML Processing
(JAXP) is needed and available from http://java.sun.com/. This API provides a small layer on top of DOM that lets you plug in different vendor s parsers without making any ’
changes to your basic code. There are many ways to do so, of which the system property is the easiest method. For example the following code permits users to specify the parser on the command line with -D option to java, and uses the Apache Xerces parser otherwise.
Introduction to Swings Java Swing is an alternative to AWT (Abstract Windowing Toolkit) but most programs which use Swing exploit AWT too. Key the following source code into the file SwingIntro.java as a first example:
import import import import import
javax.swing.JFrame; javax.swing.JPanel; javax.swing.JButton; javax.swing.JLabel; java.awt.BorderLayout;
public class SwingIntro { public static void main(String[] args)
{ MyButtons handle = new MyButtons(); handle.myMain(); } } class MyButtons
{ public void myMain() { JFrame.setDefaultLookAndFeelDecorated(true); JFrame myFrame = new JFrame("Swing Example"); myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLO SE); myFrame.setSize(300,100); JButton
jack
=
new
JButton("Jack"); JButton jill = new JButton("Jill"); JLabel myLabel = new JLabel("Please press the buttons"); JPanel topPanel = new JPanel();
topPanel.add(jack); topPanel.add(jill); JPanel botPanel = new JPanel(); botPanel.add(myLabel);
myFrame.getContentPane().add(topPanel, BorderLayout.NORTH); myFrame.getContentPane().add(botPanel, BorderLayout.SOUTH);
myFrame.setVisible(true); } } N.Sridhar, Associate Professor, IT Dept, GMRIT
Now compile it using the javac command: $ javac SwingIntro.java Then run it using the java command: $ java SwingIntro A new window appears within which there is a Swing JFrame. Ignoring the
line of dots (which are discussed below), the outline appearance might be: Outer Title bar
+----------------------------+ | |
+----------------------------+ | Inner Title bar | +----------------------------+ +------+ +------+ | | | | Jack | | Jill | | | | +------+ +------+ |. .|
| | | Please press the buttons | +----------------------------+
Unsurprisingly, pressing the buttons has no effect since ActionListeners have been supplied. They will be added later.
no
The outer title bar MAY NOT BE PRESENT. If it is, it belongs to an enclosing window which can be ignored. [This is the responsibility of the underlying window manager, perhaps an X-windows system.] Everything else is the JFrame which is the subject of this first example. The JFrame has its own title bar heading a region in which there are three JComponents, these being two JButtons and a JLabel. All the classes which Java Swing provides are in the package javax.swing (to be contrasted with AWT classes which are in the java.awt package). Many of the Swing classes begin with the letter J. Three JComponents can be laid out in various ways, perhaps in a single row or single column. Here the JFrame incorporates two JPanels: the one above the dotted line contains the two JButtons and the one below contains the JLabel. OUTLINE OF THE PROGRAM
The program begins with four import statements for the various Swing classes and a single import statement for the one AWT class that is used, BorderLayout. This will be discussed later. PRINCIPAL DETAILS OF THE PROGRAM When sketching the appearance of the required JFrame the designer might think of the following hierarchy:
N.Sridhar, Associate Professor, IT Dept, GMRIT
JFrame | +-------------+-------------+
| JPanel | +-------------+-------------+ | | JButton JButton
| JPanel | | | JLabel
Each JButton, JLabel and JPanel is a child class of JComponent (a JFrame is not) but in simple cases it is convenient to think of assembling
low-level JComponents (JButtons, JLabels and many others) into a number of JPanels. The JPanels in their turn are assembled into a single JFrame.
Swing provides many facilities for controlling layout and in this introductory example the two JButtons are to be arranged side-by-side within a JPanel and the two JPanels are to be arranged one above the other. Most of the statements in method myMain() declaring JComponents and assembling them:
are concerned with
1. JFrame myFrame = new JFrame("Swing Example"); Declare the top-level JFrame and assign it to myFrame. The String argument for the constructor will appear in the title bar. 2. JButton jack = new JButton("Jack"); JButton jill = new JButton("Jill"); JLabel myLabel = new JLabel("Please press the buttons"); Declare the three low-level JComponents, two JButtons and a JLabel, and assign them to jack, jill and myLabel respectively. The String arguments for the JButtons will appear as labels on the JButtons and the String argument for the JLabel is the text for the JLabel itself. 3. JPanel topPanel = new JPanel();
topPanel.add(jack); topPanel.add(jill); Declare the first intermediate-level JPanel, assign it to topPanel, and add the JButtons. They will appear side-by-side by default. 4. JPanel botPanel = new JPanel(); botPanel.add(myLabel); Declare the other intermediate-level JPanel, assign it to botPanel, and add the JLabel. 5. One might guess that to add the two JPanels to the JFrame all that is required is myFrame.add(topPanel); N.Sridhar, Associate Professor, IT Dept, GMRIT
myFrame.add(botPanel);
Unfortunately, adding to a JFrame is more complicated. The add() method in a JFrame object is not appropriate. Instead, one has to use the getContentPane() method in a JFrame class which returns an object which incorporates an add() method which IS the one to use. Accordingly, one might try: myFrame.getContentPane().add(topPanel) ; myFrame.getContentPane().add(botPanel) ;
These statements are still not quite right. They WILL work but adding the second panel simply obliterates the first which cannot therefore be seen. In the current example, the AWT BorderLayout layout manager is used and BorderLayout.NORTH will ensure that the first JPanel goes at the top (north) and BorderLayout.SOUTH will ensure that the second JPanel is at the bottom (south) of the JFrame. The two statements in full are: myFrame.getContentPane().add(topPanel, BorderLayout.NORTH); myFrame.getContentPane().add(botPanel, BorderLayout.SOUTH); OTHER DETAILS
OF THE PROGRAM
A window containing the JFrame can be closed in several ways. One way is to click the close button at the right-hand end of the JFrame title bar. Another way is to key Ctrl-c into the window in which the java command was given. Assuming the JFrame is still present, do the former:
Click the close button on the title bar. This title bar and its associated close button resulted from the very first statement in myMain() which invokes a class method of JFrame: JFrame.setDefaultLookAndFeelDecorated(true);
Try changing true to false or omitting the statement altogether. The program will compile and run but the JFrame will have no title bar and no close button. There may be a title bar supplied by the underlying window manager but this is not the JFrame title bar. It is usually preferable to have the JFrame title bar so restore the statement if you removed it or changed true to false. The first statement after declaring the JFrame variable myFrame is: myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
If this is omitted, the program will compile and run as before but although clicking the close button will close the window it does not stop the program running and it would be necessary to key in Ctrl-c. It is usually preferable to have this statement. The next statement is: myFrame.setSize(300,100); This sets the size of the JFrame to be 300 pixels by 100 pixels which is more than adequate to accommodate the two JButtons and the JLabel.
The last statement in method myMain() is:
myFrame.setVisible(true); If this is omitted or the true is changed to false, the program compiles and runs but the JFrame doesn’t appear and there is nothing to see. SOME EXPERIMENTS
Ensure that the program is fully restored to the original version and run it again. Then try the following experiments: 1. Click the Jack and Jill buttons. Nothing of interest happens. 2. Move the mouse pointer to the bottom-right-hand corner of the window and drag diagonally outwards. The JFrame enlarges and the two JButtons stay centrally side-by-side at the very top and the JLabel stays central at the very bottom. 3. Move the mouse pointer to the bottom-right-hand corner of the window and drag diagonally inwards to make the JFrame as small as possible. The minimum size will be just wide enough to accommodate the JLabel (which is wider than the two JButtons together) and just tall enough to accommodate the two panels, one above the other.
4. Restore the window to approximately its original size. 5. Note the symbol towards the right-hand end of the JFrame title bar which consists of a small square with an arrow pointing south-west towards one corner. This is the iconify or minimize button. Click it and the window iconifies. 6. De-iconify the window. 7. Note the symbol at the left-hand end of the JFrame title bar. This opens a menu which has two useful entries, Minimize and Close. Choose Close.
The JToolBar JToolBar is a container bar for tool buttons of the type you see in many programs. Normally, the JDK documentation recommends that you add the JToolBar as the only component on one side of a BorderLayout typically the North side), and that you not add components to the other 3 sides. The buttons you add to the toolbar are just small JButtons with picture icons and without text. The JToolBar class has two important methods: add and addSeparator. JToolBar toolbar = new JtoolBar(); JBUtton Open = new JButton(“open.gif”); toolbar .add(Open); toolbar.addSeparator();
By default, JButton has a rectangular shape. To make the usual squarelooking buttons, you need to use square icons and set the insets of the button to zero. On most toolbars, the icons are 25 x 25 pixels. We thus develop the simple ToolButton class below, which handles both the insets and the size: public class ToolButton extends JButton { public ToolButton(Icon img) { super(img); setMargin(new Insets(0,0,0,0)); setSize(25,25); } }
The JToolBar also has the characteristic that you can detach it from its anchored position along the top side of the program and attach it to another side, or leave it floating. This allows some user customization of the running program, but is otherwise not terribly useful. It also is not particularly well implemented and can be confusing to the user. Thus, we recommend that you use the setFloatable(false) method to turn this feature off.
JToggleButton The JToggleButton class is actually the parent class for check boxes and radio buttons. It is a two-state button that will stay in an up or down position when clicked, and you can use it just like a check box. While toggle buttons look sort of strange on most screens, they look very reasonable as part of toolbars. You can use individual toggle buttons to indicate the state of
actions the user might select. By themselves, toggle buttons behave lick check boxes, so you can press as many as you want, and you can “uncheck” or raise toggle buttons by using the setSelected(false) method. You can also add toggle buttons to a ButtonGroup so that they behave like radio buttons: only one at a time can be pressed down. However, once a ButtonGroup object is mediating them, you can’t raise the buttons using the setSelected method. If you want to be able to raise them, but still only allow one at a time to be pressed, you need to write your own Medator class to replace the ButtonGroup object.
Sample Code The simple program display in Figure 10-3 illustrates checkboxes, radio buttons, toolbar buttons and toggle buttons:
Figure simple display of checkboxes, radio buttons, toolbar buttons and toggle buttons. Note the “b” JToggleButton stays depressed until another button is selected, as toggle buttons are supposed to do. While the user can select any number of organizations in which he holds memberships using the JCheckboxes, he can only select one political party from the JRadioButtons.
Swing Versus AWT
The Swing components are referred to as “lightweight” components, because they don’t rely on native user-interface components. They are, in fact, 100% pure Java. Thus, a Swing JButton does not rely on a Windows button or a Motif button or a Macintosh button to implement its functionality. They also use fewer classes to achieve this interface than the
previous heavier-weight AWT classes. In addition, there are many more Swing user-interface components than there were AWT components. Swing gives us image buttons, hover buttons, tooltips, tables, trees, splitter panels, customizable dialog boxes and quite a few other components. Since Swing components create their look and feel completely within the Swing class hierarchy, you can have a pluggable look and feel to emulate Windows, Motif, Macintosh or the native Swing (metal) look. Many Swing components make use of an architecture derived from the model-view-controller design pattern. The idea of this MVC patternl, is to keep the data in a model class, display the data in a view class and vary the data and view using a controller class. We’ll see in later chapters that this is exactly how the JList and JTable handle their data.
How Swings Better than AWT You should use the Swing GUI classes whenever you want to have a professional looking user interface. The classes provide beveled borders, buttons and menus with images, tooltips, and more sophisticated lists, trees and tables. You should only avoid Swing classes in applets when some of your users may be using back-level browsers and don’t want to load the Java plug-in.
Additional facilities than AWT All Swing components inherit from the JComponent class. While JComponent is much like the AWT’s Component in its position in the hierarchy, JComponent provides the pluggable look and feel. It also provides •
Keystroke handling that works with nested components.
•
A border property that defines both the border and the component’s insets.
•
Tooltips that pop up when the mouse hovers over the component.
•
Automatic scrolling of any component when placed in a scroller container.
Because of this interaction with the user interface environment, Swing’s JComponent is actually more like the AWT’s Canvas than its Component class. You also should avoid mixing AWT and Swing components in the same program, since they do not interact well. Difference in Creation
Swings getContentPane().setLayout(new JButton
b
=
new
JButton
getContentPane().add(b);
//add
AWT Button
b
add(b);
=
new
Button
BorderLayout());
(“Hi”);
(“Hi”);
button
to
layout
1. Introduction to JavaBeans To start with I would like to quote a sentence from the official JavaBeans API specification, which gives a first definition of what JavaBeans are: “ A JavaBean JavaBean is a reusable reusable software software component component that that can be manipulated manipulated visuall visually y in a builde builderr tool” tool” .
Even though this quote seams to be quite simple, we have to clarify what is meant by software component and builder tool in order to be able to fully understand the quote s statement. ’
’
’
’
’
Definition of a software component Software components are self-contained software units developed according to the motto “ Developed Developed them them once, once, run run and reused reused them every everywhere where”” . Or in other other words, words, reusabil reusability ity is the main concern behind the component model. Software components offer predefined services other components components or applications applications can make use of. To facilitate the use of these services components have to adhere to a given specification. As Robert Englander outlin outlines es in his book book “ Develop Developing ing JavaBea JavaBeans” ns” (1997) (1997) [2], [2], softwar software e compone components nts “ must behave in ways that are expected. It s like a society society of software software citizens. The citizens citizens (components) bring functionality, while the society (environment) brings structure and ’
order. order.””
JavaBea JavaBeans ns is is one of Sun Sun s component models. The first specification appeared on ’
December 1996. As Java runs on various various platforms the provision of a platform neutral component model was one of Sun s main concerns. JavaBeans offer their services by ’
exposing properties, events and methods. The features a component exposes can be
manipulated visually in a builder tool. This leads us directly to the next definition. Namely the one of what a builder tool is.
Definition of a builder tool Builder tools allow a developer to work with JavaBeans in a conve convenient nient way. By examining a JavaBean by a process known as Introspection, a builder tool exposes the discovered features of the JavaBean for visual manipulation. A builder tool maintains a list of all JavaBeans available. It allows you to compose the Bean into applets, application, servlets and composite components (e.g. a JFrame), customize its behavior and appearance by modifying its properties and connect other components to the event of the Bean or vice versa. Although the previous sentence might might imply that a JavaBean JavaBean is a visual component, it has not necessarily to be one, as you will see in the following section. There are different builder tools available, varying in functionality. The plugin Visual Editor for Eclipse has a moderate support of JavaBeans compared to NetBeans ’
’
: NetBean s provided property sheet to customize the FontSelector ’
which even allows you to develop an application consisting of JavaBeans without writing a single line line of code (in best case). Figure Figure depicts the property property sheet provided provided by NetBeans when the FontSelector JavaBean is selected.
Advantages Advantag Advantages es of Java Beans Beans 1. Resuable 2. Arch 2. Architec itectur ture e Neutr Neutral al 3. Simple to Use JavaBeans can appear in two forms: visual and non visual. The former is more common, as the majority of JavaBeans are extensions of AWT or Swing components. Some Beans may be simple GUI elements such as a specialized specialized button or slider. Others may be sophisticated visual software components such as a diagram component offering different ways to present the bounded data. They all have in common that they can be customized at design time in a builder tool. However, as already mentioned, JavaBeans can also be non visual yet still be customizable using a builder-tool. They do not have to be derived from a specific class or interface, albeit it is recommended that they implement the Serializable interface. In respect of the source code there is no real difference difference noticeable between between a JavaBean JavaBean and a “ normal” normal” Java class. Basically Basically what what differ differs s the source code of a “ normal” normal” class from from a JavaBean JavaBean is that the the latter latter adhere adheres s to the the JavaBeans JavaBeans API specification in terms of how it is developed. Only the strict adherence to this specification ensures that builder tools can assist a developer to work with JavaBeans. Thus for many solutions you can either develop a conventional Java class where you have to write your own code for setting its properties or you can build a JavaBean which allows you to set the properties properties through the builder tool. Eventually both methods result in code just that in the latter case the builder tool creates the code automatically in the background according to your selection(s). And as already already mentioned in the the best case you do not even have to write a single line of code.
JavaBeans basic rules A JavaBe JavaBean an should should:: •
be public
•
implemen implementt the the Serializa Serializable ble interfac interface e
•
have have a no-a no-arg rg constr construct uctor or
•
be derived derived from javax.swing. javax.swing.JComponen JComponentt or java.awt.Comp java.awt.Component onent if it is visual visual
BDK Instrospection As described described in the introduction introduction,, builder builder tools typically provide a property property sheet sheet where one one can conveniently set the properties of a JavaBean component or connect event listeners to an exposed event. In order to provide this service a builder tool needs to examine the component for its features (=properties, events and methods). This process is referred to as “ introsp introspectio ection” n” . To obtain obtain info informat rmation ion about about a specific specific JavaBea JavaBean n one one can can use the static getBeanInfo() method of the Introspector class. This method returns an instance of the BeanInfo class, which describes all features a JavaBean exposes. The use of this method is shown in the following code fragment: FontSelector fs = new FontSelector ( ) ; BeanInfo bi = Introspector . getBeanInfo
( fs . getClass ( ) ) ;
The most important methods of the BeanInfo object will now be described in the following following sections.
Methods of the BeanInfo class Events To determine what events a JavaBean can fire, the BeanInfo object provides the method
getEventSetDescriptors getEventS etDescriptors which returns an array of EventSetDescriptors. EventSetDe scriptors. As the name suggests an EventSetDescriptor is a class describing an event. In our example we use the getName() method to display the name of the events. EventSetDescriptor [ ] esd
= bi . getEventSetDescriptors ( ) ;
for ( int i = 0 ;i < esd . length ;i + + ) { System . out . print ( esd [i ] . getName ( ) }
+ " ");
Properties The determination of properties works similar to the event determination. The method
getPropertyDescriptors() returns an array of PropertyDescriptors reporting all properties of a Bean. PropertyDescriptor pd [ ]
= bi . getPropertyDescriptors ( ) ;
for ( int i = 0 ;i < pd . length ;i + + ) System . out . print ( pd [i ] . getName ( )
+ " ");
Method It s no surprise that the methods determination works the same way just with d ifferent names. Note, that only public methods of the Bean will be reported. For each method ’
descriptor, the parameters types for the described methods can be discovered through the getParameterDescriptors() method. MethodDescriptor md [ ]
= bi . getMethodDescriptors ( ) ;
for ( int i = 0 ;i < md . length ;i + + ) System . out . print ( md [i ] . getName ( )
+ " ");
Beans Architecture
Properties Properties are named public attributes which determine the internal state of a JavaBean and thus its behavior and appearance. For example, a GUI textfield might have a property named maxlength which restricts the number of characters one can insert into the texfield. There exist four different kinds of properties a JavaBean can expose: ’
•
Simple
•
Indexed
•
Bound
•
Constrained
’
All types of property have in common that they are characterized by a pair of set/get methods. A getter method is used to read the value of a readable property. Its name has to start with get, as you will see later on. To update the property s value a setter method has to be called. Analog to the getter-method its name has to start with set. If a property should be read-only the latter method has to be left out. On the other side, if the property should be write-only no getter-method has to be written. The property s name is determined by the name used in the pair of methods. Typically the value of a ’
’
property is stored in a private instance variable whose name matches the name of the property. But this is not a requirement. How you name the instance variable used in conjunction with the property is up to you.
The data type of the property can be a built-in type as well as a custom class or interface. The following code snippet shows the naming pattern for the discussed set/get methods: public void set < Propertyname >( < Datatype > param ) { ? } public
Changing a property may also lead to other actions. For instance, updating the background color property of a JavaBean might cause a repainting of the bean with its new color.
Bound Properties Bound properties notify other components of their changed value. JavaBeans follow the Observer pattern to do this notification. Properties notifying observers about their changes are referred to as bound properties, as most observers bind a special behaviour to property changes.
In order to provide this notification service a JavaBean needs to have the following two methods: public void addPropertyChangeListener ( PropertyChangeListener p ) { changes . addPropertyChangeListener (p ) ; } public void removePropertyChangeListener ( PropertyChangeListener p ) changes . removePropertyChangeListener (p ) ; }
{
PropertyChangeListener is an interface declared in the java.beans package. Observers which want to be notified of property changes have to implement this interface, which consists of only one method: public interface PropertyChangeListener extends EventListener public void propertyChange ( PropertyChangeE vent e ) ; }
{
Even though we could organize the list of observers by ourselves the beans package offers a class named PropertyChangeSupport which takes over this job for us. Apart
from having methods to add/remove listeners it possesses various methods to notify the registered observers about a changed property. In the code shown above the variable referring an instance of this support class is named changes. The declaration code for it looks like this: private PropertyChangeSupport
changes
= new PropertyChangeSupport
( this ) ;
Our FontSelector informs about changes of the currently selected font. Of course it would be wise to make any property a bound property. However in our case we limit it to one property in order to keep it simple. As you will see in the code below, firing a notification requires the information which property has changed, what its old value was and what its new value is. private boolean bold ; public void setBold ( boolean bold ) { if ( this . bold ! = bold ) { boolean wasBold = this . bold ; this . bold = bold ; boldBtn . setSelected ( bold ) ; / / Notify PropertyChangeList eners : firePropertyChange (" bold " , wasBold , bold ) ; } }
public boolean isBold ( ) { return bold ; }
At this stage it is important to note that in this example there is only one instance for all bound properties. Thus an observer can not enroll just for the notification of a particular property. All enrolled observers are notified of any changed bound properties. As a result, every observer needs to check on notification whether the property that changed was the one the observer was expecting or something else. This can be easily done by using the getPropertyName() method of the PropertyChangeEvent object which is passed to the observer.
Simple Properties As the name suggests, simple properties are the simplest of the four. For our FontSelector we would like to have a default font size, which will be used as initial font size at runtime. The following code sample shows how this property is defined: private String name
=
‘
‘
FontSelector
’
’
;
public void setName ( String name ) { this . name = name ; }
public String getName ( ) { return name ; }
If the property s data-type is boolean the get methods can also be named is
private boolean enabled ; public void setEnabled ( boolean enabled ) { this . enabled = enabled ; } public boolean isEnabled ( ) { return enabled ; }
N.Sridhar, Associate Professor, GMRIT, Rajam
Personalized Bean information with BeanInfo By default an Introspector uses the Reflection API to determine the features of a JavaBean. However, a JavaBean can provide its own BeanInfo which will be used instead by the Introspector to determine the discussed information. This allows a developer hiding specific properties, events and methods from a builder tool or from any other tool which uses the Introspector class. Moreover it allows supplying further details about events/properties/methods as you are in charge of creating the descriptor objects. Hence you can, for example, call the setShortDescription() method to set a descriptive description. A BeanInfo class has to be derived from the SimpleBeanInfo class and its name has to start with the name of the associated JavaBean. At this point it has to be underlined that the name of the BeanInfo class is the only relation between a JavaBean and its BeanInfo class. If in the example below the class was named FontSelector_BeanInfo (note the underline) the Introspector would not recognize the class as BeanInfo class of the FontSelector JavaBean. import java . beans . * ; public class FontSelectorBeanInfo extends SimpleBeanInfo { private final static Class beanClass = FontSelector . class ;
public Proper tyDescriptor [ ] getProp ertyDescriptor s ( ) { PropertyDescriptor [ ] properties = new PropertyDescriptor [ 3 ] ; try { properties [ 0 ] = new PropertyDescriptor ( " fontSize " , FontSelector . class properties [ 0 ] . setConstrained ( Boolean . TRUE ) ; properties [ 0 ] . setDisplayName (" Fontsize " ) ; properties [ 1 ] = new PropertyDescriptor ( " bold " , FontSelector . class properties [ 1 ] . setBound ( Boolean . TRUE ) ; properties [ 1 ] . setDisplayName (" Bold " ) ;
);
properties [ 2 ] = new PropertyDescriptor ( " fonts " , FontSelector . class ); properties [ 2 ] . setDisplayName (" Fonts " ) ; properties [ 2 ] . setShortDescription (" List of fonts one can choose from " ) ; } catch ( IntrospectionException e ) e . printStackTrace ( ) ; } return properties ; } }
{
);
Constrained Properties Constrained properties are similar to bound properties. But in addition to the latter, constrained properties inform observers also about an imminent property change. This allows an observer to inhibit the change of the property by throwing a PropertyVetoException, in case the observer does not agree to this change. As the observers which are able to put a veto on a property change do not necessarily have to be the same which are notified after the change, a JavaBean which is constrained as well as bound has to maintain two lists of listeners: a list of PropertyChangeListener and a list of VetoableChangeListener.
private
VetoableChangeSupport
vetoes
= new VetoableChangeSupport
( this ) ;
public void addVetoableChangeListener ( VetoableChangeLis tener v ) { vetoes . addVetoableChangeListener (v ) ; } public void removeVetoableChangeListener ( VetoableChangeListen er v ) vetoes . removeVetoableChangeListener (v ) ; }
{
By convention the set method must not handle the exception a vetoed observer threw, rather it has to be forwarded. Hence the set method has to be declared to throw the PropertyVetoException exception. private int currentFontSize; private int fontSize
= 11 ;
public void setFontSize ( int newFontSize ) throws PropertyVetoException if ( fontSize ! = newFontSize ) { / / Notify VetoableChangeListener : fireVetoableChange (" fontSize " , fontSize , newFontSize ) ;
{
int oldFontSize = fontSize ; fontSize = newFontSize ; / / Raise event : ( see chapter Events & Methods ) notifyFontSizeChangedListeners ( newFontSize ) ;
/ / It s good pr actise to be a bound property as well : firePropertyChange (" fontSize " , oldFontSize , newFontSize ) ; ’
} } public int getFontSize ( ) return fontSize ; }
{
As shown in the code above the property does not only notify the VetoableChangeListeners but also the PropertyChangeListeners after the change has been performed. Even though not beeing required it is recommend by the specification that if a property is constrained it is also bound.
N.Sridhar, Associate Professor, GMRIT, Rajam
Indexed Properties If a simple property can hold an array of value they are no longer called simple but instead indexed properties. The method s signature has to be adapted accordingly. An indexed property may expose set/get methods to read/write one element in the array (so-called index getter/setter ) and/or so-called array getter/setter which read/write the entire array. ’
’
’
’
’
public PropertyType getPropertyName ( int position ) / / indexed getter public void setPropertyName ( PropertyType element , int position ) / / indexed setter
public PropertyType [ ] getPropertyName ( ) / / array getter public void setPropertyName ( PropertyType [ ] list ) / / array setter
The indexed methods may throw a java.lang.ArrayIndexOutOfBoundsException runtime exception in case the requested index is outside the current array bounds. Our FontSelector bean has a string array of font names a user can choose from. The code for that looks as following: private String [ ] fonts ; public void setFonts ( String [ ] fonts ) { this . fonts = f onts ; } public String [ ] getFonts ( ) return fonts ; }
{
public String getFonts ( int pos ) { return fonts [ pos ] ;
N.Sridhar, Associate Professor, GMRIT, Rajam
}
Persistence “ Persistence is the ability of an object to store its state.” [5] JavaBeans use the Java Serialization API to gain this ability. The simplest way to enable serialization of a Bean
is by implementing the Serializable interface. The points you have to consider when declaring a Bean to be serializable are the same as for any other serializable object (e.g. how to deal with transient and static variables, whether a validation is required upon deserialization etc.). The greatest strength of persistence regarding JavaBeans lies in the ability to create prototypes a new JavaBean can be instantiated from. For example, a Java application using our FontSelector Bean can serialize the Bean on a Microsoft Windows machine, the serialized file can be sent to a Linux machine, where another Java application can instantiate a new Bean with the exact state (same fonts, fontsize etc.) its counterpart had on the Windows machine. Although serialization is a convenient way to persist a JavaBean this method has a downside. Serialization is intended to persist an object for a short time, for example to transfer the object to a remote machine through Java RMI (Remote Machine Invocation). But JavaBeans are typically persisted to serve as a prototype that requires a mechanism for long-term persistence. The solution is to represent the JavaBean in a XML structure, which is topic of the next section.
Long-term persistence The beans package supplies the XMLEncoder class, which enables JavaBeans to be saved in XML format. The use of the XMLEncoder is pretty straightforward. The following code
fragment illustrates how to generate a XML file representing a FontSelector class instance with its internal state. FontSelector fs
= new FontSelector ( ) ;
fs . setFontSize ( 1 4 ) ; fs . setFonts ( new String [ ]
{" Arial " , " Verdana " , " Times " } ) ;
XMLEncoder encoder = new XMLEncoder ( new BufferedOutputStream ( new FileOutputStream ( " FontSelector . xml "
encoder . writeObject ( fs encoder . close ( ) ;
);
N. Sridhar, Associate Professor, GMRIT
) ) );
The resulting XML file looks as follows: xml version =" 1 . 0 " encoding ="UTF - 8 " ? >
Bean Reconstitution
A JavaBean is reconstituted from its serialized state by deserialization. This can be either done in conventional fashion by using a ObjectInputStream object or by using the static java.beans.Beans.instantiate() method, which most builder tools make use of. This method looks for a file named ¡Classname¿.ser. If such a file exists the class is deserialized and returned. Otherwise, the Bean will be created by using the default no-arg constructor of the class. FontSelector fs
= ( FontSelector ) Beans . instantiate ( null , " FontSelector " ) ;
However, this method can not be used if a bean has been stored in XML format. In this case the XMLDecoder class has to be used for reading the XML file and returning the reconstituted object. XMLDecoder decoder = new XMLDecoder ( new BufferedInputStream (
new FileInputStrea m ( " FontSelector . xml " ) ) ) ; FontSelector fs = ( FontSelector ) de coder . readObject ( ) ; decoder . close ( ) ;
The Beans API class The Beans class found in java.beans is not meant to be instantiated. It consists of a few static methods, the most interesting of them (from my point of view) will be outlined in the following. The instantiate() method has already been mentioned in chapter 5.2. As its name suggests, the method isDesignTime() informs about whether a JavaBean is running at design-time, viz, running inside an application builder environment. Using this method allows a JavaBean, for example, having different appearances at design- and run-time. The setDesignTime() method is used to set the environment a JavaBean is
running in. Similarly, setGuiAvailable() and isGuiAvailable() sets and informs whether a GUI is available (depends on the Java Virtual Machine).
BeanContext A set of related JavaBeans can be logically grouped into a “ context” . This context can be thought of as a “ containing environment” and is known as BeanContext. As stated in Sun s tutorial about using the BeanContext API, there are two distinct types of BeanContexts: one which supports membership only (interface ’
java.beans.beancontext.BeanContext) and one which supports membership and offers services (interface java.beans.beancontext.BeanContextServices) to its JavaBeans nested within. BeanContexts follow the Composite Pattern, where BeanContext classes are the composite. Thus a BeanContext can not only host JavaBeans but also BeanContext objects.
Java Servlets
I have presented a Java servlet example before to give you a sense of what a servlet looks like. From the example, you can get to know a servlet has methods like doGet(), doPost(), etc. to deal with different kinds of HTTP requests. But H TTP is not the only thing that servlets can do about. Actually they may communicate with other networking components based on various protocols. This note discusses the properties of generic servlets.
Servlet Life Cycle To better understand the behavior of servlets, let s take a look at the life cycle of servlets. ’
A servlet is basically a small Java program that runs within a Web server. It can receive requests from clients and return responses. The whole life cycle of a servlet breaks up into 3 phases:
•
Initialization: A servlet is first loaded and initialized usually when it is requested by the
corresponding clients. Some websites allow the users to load and initialize servlets when the server is started up so that the first request will get responded more quickly. •
Service: After initialization, the servlets serve clients on request, implementing the
application logic of the web application they belong to. •
Destruction: When all pending requests are processed and the servlets have been idle
for a specific amount of time, they may be destroyed by the server and release all the resources they occupy.
More specifically, the behavior of a servlet is described in javax.servlet.Servlet interface, in which the following methods are defined:
•
public void init(ServletConfig config) throws ServletException This method is called once when the servlet is loaded into the servlet engine, before the servlet is asked to process its first request. The init method has a ServletConfig parameter. The servlet can read its initialization arguments through the ServletConfig object. How the initialization arguments are set is servlet engine dependent but they are usually defined in a configuration file. N. Sridhar, Associate Professor, GMRIT
A typical example of an initialization argument is a database identifier. A servlet can
read this argument from the ServletConfig at initialization and then use it later to open a connection to the database during processing of a request: private String databaseURL;
public void init(ServletConfig config) throws ServletException { super.init(config); databaseURL = config.getInitParameter("database"); }
• public void service(ServletRequest request, ServletResponse response) throws
ServletException, IOException This method is called to process a request. It can be called zero, one or many times until the servlet is unloaded. Once a servlet is loaded, it remains in the server s memory as a single object instance. Thereafter, the server invokes the servlet to handle a request using a simple, lightweight method invocation. Unlike with CGI, there s no process to spawn or interpreter to invoke, so the servlet can begin handling the request almost immediately. Multiple, concurrent requests are handled by separate threads, so servlets are highly scalable. Servlets are naturally enduring objects. Because a servlet stays in the server s memory as a single object instance, it automatically maintains its state and can hold on to external resources, such as database connections, that may otherwise take several seconds to establish. The following servlet presents information about how many times it has been accessed: ’
’
’
import java.io.*; import javax.servlet.*;
import javax.servlet.http.*; public class SimpleCounter extends HttpServlet {
int count = 0; public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/plain"); PrintWriter out = res.getWriter(); count++; out.println("Since loading, this servlet has been accessed " + count + " times."); } }
N. Sridhar, Associate Professor, GMRIT
The variable count is shared by all the threads each corresponding to a single request. So this provides a way for the threads to communicate with each other. Unfortunately the concurrency that multiple threads (one per request) can execute this method in parallel also brings problems. Imagine that one thread increments the count and just afterward, before the first thread prints the count, the second thread also increments the count. Each thread will print the same count value, after effectively increasing its value by 2. The order of execution goes something like this: count++
// Thread 1
count++
// Thread 2
out.println
// Thread 1
out.println
// Thread 2
Now, in this case, the inconsistency is not a real problem, but many other servlets have more serious opportunities for errors. To prevent these types of problems and the inconsistencies that come with them, we can add one or more synchronized blocks to the code. Anything inside a synchronized block or a synchronized method is guaranteed not to be executed concurrently by another thread. Before any thread begins to execute synchronized code, it must obtain a monitor (lock) on a specified object instance. If another thread already has that monitor - because it is already executing the same synchronized block or some other block with the same monitor - the first thread must wait. For example, we may wrap the addition and print operations in a synchronized blocks as follows: PrintWriter out = res.getWriter();
synchronized(this) { count++; out.println("Since loading, this servlet has been accessed " + count + " times."); }
The javax.servlet.SingleThreadModel interface provides another approach to avoid race condition. If a servlet implements this interface, you are guaranteed that no two threads will execute concurrently in the servlet s service method. ’
The servlet container can make this
guarantee by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet. If a servlet implements this interface, the servlet will be thread safe. However, this interface does not prevent synchronization problems that result from servlets accessing shared resources such as static class variables or classes outside the scope of the servlet. •
public void destroy() This method is called once just before the servlet is unloaded and taken out of service. The following gives an servlet example with both init() and destroy() methods:
N. Sridhar, Associate Professor, GMRIT
USAGE of Servlet API import java.io.*; import javax.servlet.*;
import javax.servlet.http.*; public class InitDestroyCounter extends HttpServlet {
int count;
public void init() throws ServletException { // Try to load the initial count from our saved persistent state FileReader fileReader = null; BufferedReader bufferedReader = null; try { fileReader = new FileReader("InitDestroyCounter.initial"); bufferedReader = new BufferedReader(fileReader);
String initial = bufferedReader.readLine(); count = Integer.parseInt(initial); return; } catch (FileNotFoundException ignored) { }
//
no saved state
catch (IOException ignored) { }
//
problem during read
catch (NumberFormatException ignored) { } finally { // Make sure to close the file try {
//
corrupt saved state
if (bufferedReader != null) { bufferedReader.close(); }
} catch (IOException ignored) { } } // No luck with the saved state, check for an
init parameter
String initial = getInitParameter("initial"); try { count = Integer.parseInt(initial); return; } catch (NumberFormatException ignored) { } // null or non-integer value // Default to an initial count of "0"
count = 0; } public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException { //... }
public void destroy() {
super.destroy();
// entirely optional
saveState(); }
public void saveState() { // Try to save the accumulated count FileWriter fileWriter = null; PrintWriter printWriter = null;
try { fileWriter
=
new
FileWriter("InitDestroyCounter.initial");
printWriter = new PrintWriter(fileWriter);
printWriter.println(count); return; } catch (IOException e) {
// problem during write
// Log the exception. } finally { // Make sure to close the file
if (printWriter != null) { printWriter.close(); } } } }
Each time this servlet is unloaded, it saves its state in a file named InitDestroyCounter.initial. In the absence of a supplied path, the file is saved in the server process s current directory, ’
usually the startup directory.
Request Parameters Besides the URI and headers, a request message can contain additional information in the form of parameters. If the URI identifies a server-side program for displaying weather information, for example, request parameters can provide information about which city the user wants to see a forecast for. In an e-commerce application, the URI may identify a program that processes orders, with the user's customer number and the list of items to be purchased transferred as parameters. Parameters can be sent in one of two ways: tacked on to the URI in the form of a query string, or sent as part of the request message body. This is an example of a URL with a query string: http://www.weather.com/forecast?city=Hermosa+Beach&state=CA
The query string starts with a question mark ( ?) and consists of name/value pairs separated by ampersands ( &). These names and values must be URL-encoded , meaning that special characters, such as whitespace, question marks, ampersands, and all other nonalphanumeric characters are encoded so that they don't get confused with characters used to separate name/value pairs and other parts of the URI. In this example, the space between Hermosa and Beach is encoded as a plus sign. Other special characters are encoded as their corresponding hexadecimal ASCII value: for instance, a question mark is encoded as %3F. When parameters are sent as part of the request body, they follow the same syntax; URL encoded name/value pairs separated by ampersands.
Request Methods As described earlier, GET is the most commonly used request method, intended to retrieve a resource without causing anything else to happen on the server. The POST method is almost as common as GET; it requests some kind of processing on the server, for instance, updating a database or processing a purchase order. The way parameters are transferred is one of the most obvious differences between the GET and POST request methods. A GET request always uses a query string to send parameter values, while a POST request always sends them as part of the body (additionally, it can send some parameters as a query string, just to make life interesting). If you insert a link in an HTML page using an element, clicking on the link results in a GET request being sent to the server. Since the GET request uses a query string to pass parameters, you can include hardcoded parameter values in the link URI: Hermosa Beach weather forecast
When you use a form to send user input to the server, you can specify whether to use the GET or POST method with the method attribute, as shown here:
If the user enters "Hermosa Beach" and "CA" in the form fields and clicks on the button, the browser sends a request message like this to the server: POST /forecast HTTP/1.0 Host: www.gefionsoftware.com
Submit
User-Agent: Mozilla/4.5 [en] (WinNT; I) Accept: image/gif, image/jpeg, image/pjpeg, image/png, */* Accept-language: en Accept-charset: iso-8859-1,*,utf-8 city=Hermosa+Beach&state=CA
Due to the differences in how parameters are sent by GET and POST requests, as well as the differences in their intended purpose, browsers handle the requests in different ways. A GET request, parameters and all, can easily be saved as a bookmark, hardcoded as a link, and the response cached by the browser. Also, the browser knows that no damage is done if it needs to send a GET request again automatically, for instance if the user clicks the Reload button. A POST request, on the other hand, can't be bookmarked as easily; the browser would have to save both the URI and the request message body. Since a POST request is intended to perform some possibly irreversible action on the server, the browser must also ask the user if it's okay to send the request again. You have probably seen this type of confirmation dialog
N.Sridhar, Associate Professor, GMRIT, Rajam
Javax. avax.servlet servlet package basics
Servlet Context All servlets belong to one servlet context. The javax.servlet.ServletContext interface in the
Java Servlet API is responsible for the state of its servlets and knows about resources and attributes available to the servlets in the context. Here we will only look at how ServletContext attributes can be used to share information among a group of servlets. There are three ServletContext methods dealing with context attributes: getAttribute,
setAttribute and removeAttribute. In addition the servlet engine may provide ways to configure a servlet context with initial attribute values. This serves as a welcome addition to the servlet initialization arguments for configuration information used by a group of servlets, for instance a database identifier , a style sheet URL for an application, the name of a mail server, etc. A servlet gets a reference to its ServletContext object through the ServletConfig object. The HttpServlet class actually provides a convenience method (through its superclass GenericServlet) named getServletContext to make it really easy: ServletContext context = getServletContext(); String styleSheet = request.getParameter("stylesheet"); if (styleSheet != null) { // Specify a new style sheet for the application
context.setAttribute("stylesheet",
styleSheet);
}
The code above could be part of an application configuration servlet, processing the request from an HTML FORM where a new style sheet can be specified for the application. All servlets in the application that generate HTML can then use the style sheet attribute like this: ServletContext context = getServletContext(); String
styleSheet
=
context.getAttribute("stylesheet");
out.println(""); out.println("");
Session Tracking and Security Issues An HttpSession class was introduced in the Servlet API. Instances of this class can hold information for one user session between requests. You start a new session by requesting an HttpSession object from the HttpServletRequest in your doGet or doPost method: HttpSession session = request.getSession(true);
This method takes a boolean argument. true means a new session shall be started if none
exist, while false only returns an existing session. The HttpSession object is unique for one user session. The Servlet API supports two ways to associate multiple requests with a session: cookies and URL rewriting. If cookies are used, a cookie with a unique session ID is sent to the client when the session is established. The client then includes the cookie in all subsequent requests so the servlet engine can figure out which session the request is associated with. URL rewriting is intended for clients that don t support cookies or when the user has ’
disabled cookies. With URL rewriting the session ID is encoded in the URLs your servlet sends to the client. When the user clicks on an encoded URL, the session ID is sent to the server where it can be extracted and the request associated with the correct session as above. To use URL rewriting you must make sure all URLs that you send to the client are encoded with the encodeURL or encodeRedirectURL methods in HttpServletResponse. An HttpSession can store any type of object. A typical example is a database connection allowing multiple requests to be part of the same database transaction, or information about purchased products in a shopping cart application so the user can add items to the cart while browsing through the site. To save an object in an HttpSession you use the putValue method:
Connection con = driver.getConnection(databaseURL, user, password);
session.putValue("myappl.connection", con);
In another servlet, or the same servlet processing another request, you can get the object with the getValue method: HttpSession session = request.getSession(true); Connection c on = (Connection) s ession.getValue("myappl.connection"); if (con != null) { // Continue the database transaction
You can explicitly terminate (invalidate) a session with the invalidate method or let it be timed-out by the servlet engine. The session times out if no request associated with the session is received within a specified interval.
Most servlet engines allow you to specify the
length of the interval through a configuration option. In the Servlet API there s also a ’
setMaxInactiveInterval so you can adjust the interval to meet the needs of each individual application. N. Sridhar, Associate Professor, GMRIT
Handling Cookies Cookies are small bits of textual information that a Web server sends to a browser and that the browser returns unchanged when visiting the same Web site or domain later. By having the server read information it sent the client previously, the site can provide visitors with a number
of conveniences: •
Identifying a user during an e-commerce session. Many on-line stores use a ” shopping
cart” metaphor in which the user selects an item, adds it to his shopping cart, then
continues shopping. Since the HTTP connection is closed after each page is sent, when the user selects a new item for his cart, how does the store know that he is the same user that put the previous item in his cart? Cookies are a good way of accomplishing this. In fact, this is so useful that servlets have an API specifically for this, and servlet authors don t need to manipulate cookies directly to make use of it. This is discussed later in ’
section 4. •
Avoiding username and password. Many large sites require you to register in order to use
their services, but it is inconvenient to remember the username and password. Cookies are a good alternative for low-security sites. When a user registers, a cookie is sent with a unique user ID. When the client reconnects at a later date, the user ID is returned, the server
looks it up, determines it belongs to a registered user, and doesn t require an explicit ’
username and password. •
Customizing a site. Many portal sites let you customize the look of the main page. They
use cookies to remember what you wanted, so that you get that result initially next time.
The Servlet Cookie API To send cookies to the client, a servlet would create one or more cookies with the appropriate names and values via new Cookie(name, value), set any desired optional attributes via cookie.setXxx, and add the cookies to the response headers via response.addCookie(cookie). To read incoming cookies, call request.getCookies(), which returns an array of Cookie ob jects. In most cases, you loop down this array until you find the one whose name (getName) matches the name you have in mind, then call getValue on that Cookie to see the value
associated with that name.
N. Sridhar, Associate Professor, GMRIT
Creating Cookies A Cookie is created by calling the Cookie constructor, which takes two strings: the cookie name and the cookie value. Neither the name nor the value should contain whitespace or any of: [ ] ( ) = , " / ? @ : ;
Reading and Specifying Cookie Attributes Before adding the cookie to the outgoing headers, you can look up or set attributes of the
cookie. Here s a summary: ’
•
getComment/setComment Gets/sets a comment associated with this cookie.
•
getDomain/setDomain Gets/sets the domain to which cookie applies. Normally, cookies are returned only to the exact host name that sent them. You can use this method to instruct the browser to return them to other hosts within the same domain.
•
getMaxAge/setMaxAge Gets/sets how much time (in seconds) should elapse before the cookie expires. If you don t set this, the cookie will last only for the current session (i.e. until the user quits ’
the browser), and will not be stored on disk. See the LongLivedCookie class below,
which defines a subclass of Cookie with a maximum age automatically set one year in the future. getName/setName Gets/sets the name of the cookie. virtually always care about.
The name and the value are the two pieces you
Since the getCookies method of HttpServletRequest
r eturns an array of Cookie objects, it is common to loop down this array until you have a
particular name, then check the value with getValue. See the getCookieValue method shown below.
N. Sridhar, Associate Professor, GMRIT
•
getPath/setPath Gets/sets the path to which this cookie applies. If you don t specify a path, the cookie ’
is returned for all URLs in the same directory as the current page as well as all sub-
directories. This method can be used to specify something more general. For example, someCookie.setPath("/") specifies that all pages on the server should receive the cookie. Note that the path specified must include the current directory. •
getSecure/setSecure
Gets/sets the boolean value indicating whether the cookie should only be sent over encrypted (i.e. SSL) connections. •
getValue/setValue Gets/sets the value associated with the cookie. Again, the name and the value are the two parts of a cookie that you almost always care about, although in a few cases a name is used as a boolean flag, and its value is ignored (i.e the existence of the name means true).
•
getVersion/setVersion Gets/sets the cookie protocol version this cookie complies with. Version 0, the default, adheres to the original Netscape specification.
Version 1, not yet widely supported,
adheres to RFC 2109.
Placing Cookies in the Response Headers The cookie is added to the Set-Cookie response header by means of the addCookie method of HttpServletResponse. Here s an example: ’
Cookie userCookie = new Cookie("user", "uid1234"); response.addCookie(userCookie);
Reading Cookies from the Client To send cookies to the client, you created a Cookie then used addCookie to send a Set-Cookie HTTP response header. To read the cookies that come back from the client, you call getCookies on the HttpServletRequest. This returns an array of Cookie objects corresponding to the
values that came in on the Cookie HTTP request header. Once you have this array, you typically loop down it, calling getName on each Cookie until you find one matching the name you have in mind. You then call getValue on the matching Cookie, doing some processing specific to the resultant value. This is such a common process that the following section presents a simple getCookieValue method that, given the array of cookies, a name, and a default value, returns the value of the cookie matching the name, or, if there is no such cookie, the designated default value.
N. Sridhar, Associate Professor, GMRIT
Setting Up JSP Environment with Tomcat Server
Introduction Tomcat is a Java servlet container and web server from the Jakarta project of the Apache
Software Foundation (http://jakarta.apache.org). A web server is, of course, the program
that dishes out web pages in response to requests from a user sitting at a web browser. But web servers aren t limited to serving up static HTML pages; they can also run programs in ’
response to user requests and return the dynamic results to the user s browser. This is an ’
aspect of the web that Apache s Tomcat is very good at because Tomcat provides both Java ’
servlet and Java Server Pages (JSP ) technologies (in addition to traditional static pages and external CGI programming). The result is that Tomcat is a good choice for use as a web server for many applications. And it s a very good choice if you want a free, open source ’
(http://opensource.org/) servlet and JSP engine.
Tomcat can be used stand-alone, but it is often used “ behind” traditional web servers such as Apache httpd, with the traditional server serving static pages and Tomcat serving dynamic servlet and JSP requests. No matter what we call Tomcat, a Java servlet container or servlet and JSP engine, we mean Tomcat provides an environment in which servlets can run and JSP can be processed. Similarly, we can absolutely say a CGI-enabled Web server is a CGI program container or engine since the server can accommodate CGI programs and communicate with them according to CGI specification. Between Tomcat and the servlets and JSP code residing on it, there is also a standard regulating their interaction, servlet and JSP specification, which is in turn a part of Sun s J2EE (Java 2 Enterprise Edition). ’
But what are servlets and JSP? Why do we need them? Let s take a look at them in the ’
following subsections before we cover them in much more detail in the future.
N. Sridhar, Associate Professor, GMRIT
Web Applications of Servlets and JSP Advantages Traditionally, before Java servlets, when we mention web applications, we mean a collection of static HTML pages and a few CGI scripts to generate the dynamic content portions of the web application, which were mostly written in C/C++ or Perl. Those CGI scripts could be written in a platform-independent way, although they didn t need to be ’
(and for that reason often
weren t). Also, since CGI was an accepted industry standard across all web server brands and ’
implementations, CGI scripts could be written to be web server implementation-independent. In practice, some are and some aren t. The biggest problem with CGI was that the design made it ’
inherently slow and unscalable. For every HTTP request to a CGI script, the OS must fork and execute a new process, and the design mandates this. When the web server is under a high traffic load, too many processes start up and shut down, causing the server machine to dedicate most of its resources to process startups and shutdowns instead of fulfilling HTTP requests. As for scalability, CGI inherently has nothing to do with it. As we know, whether command line arguments, environment variables or stdin/stdout are used for writing to or reading from CGI programs, all of them are limited to the local machine, not involving networking or distributed mechanisms at all. Contrastingly, Java servlets and their supporting environments are capable of scalability. I will talk about this in future classes. Another approach to generating dynamic content is web server modules. For instance, the Apache httpd web server allows dynamically loadable modules to run on startup. These modules can answer on pre-configured HTTP request patterns, sending dynamic content to the HTTP client/browser. This high-performance method of generating dynamic web application content has enjoyed some success over the years, but it has its issues as well. Web server modules can be written in a platform-independent way, but there is no web server implementationindependent standard for web server modulesthey re specific to the server you write them for, and probably ’
won t work on any other web server implementation. ’
Now let us take a look at the Java side. Java brought platform independence to the server, and Sun wanted to leverage that capability as part of the solution toward a fast and platformindependent web application standard. The other part of this solution was Java servlets. The idea behind servlets was to use Java s simple and powerful multithreading to answer ’
requests without starting new processes. You can now write a servlet-based web application, move it from one servlet container to another or from one computer architecture to another, and run it without any change (in fact, without even recompiling any of its code).
N. Sridhar, Associate Professor, GMRIT
Installing Servers Developments Refer to Activating Tomcat 5.0 and Servlet Applications for the instructions for installing
Tomcat on Solaris platform.
Web Applications on Tomcat Tomcat provides an implementation of both the servlet and JSP specifications. This section discusses what a web application looks like exactly on Tomcat and how we deploy it.
Layout of a Web Application As we mentioned above, a web application is a collection of static HTML files, dynamic JSPs, and servlet classes. It is defined as a hierarchy of directories and files in a standard layout. Such a hierarchy can be accessed in its ” unpacked” form, where each directory and file exists in the filesystem separately, or in a ” packed” form known as a Web ARchive, or WAR file. The former format is more useful during development, while the latter is used when you distribute your application to be installed. The top-level directory of your web application hierarchy is also the document root of your application. Here, you will place the HTML files and JSP pages that comprise your application s user interface. When the system administrator deploys your application into a particular ’
server, he or she assigns a context path to your application. Thus, if the system administrator assigns your application to the context path /catalog, then a request URI referring to
/catalog/index.html will retrieve the index.html file from your document root. When you
do experiments, you yourself are the administrator. To put your web application working on Tomcat, you create a subdirectory under Tomcat s webapps directory, which is the context ’
path where you are supposed to put your web application files. Figure 1 shows the general layout of a web application, where sample webapp is assumed to be the context of your web application. As you can see, the web pages (whether static HTML, dynamic JSP, or another dynamic templating language s content) can go in the root of a web application directory or in almost any ’
subdirectory that you like. Images often go in a /images subdirectory, but this is a convention, not a requirement. The WEB-INF directory contains several specific pieces of content. First, the classes directory is where you place Java class files, whether they are servlets or other class files used by a servlet, JSP, or other part of your application s code. Second, the lib ’
directory is where you put Java Archive (JAR) files containing packages of classes. Finally, the web.xml file is known as a deployment descriptor, which contains configuration for the web application, a description of the application, and any additional customization.
When you install an application into Tomcat, the classes in the WEB-INF/classes/ directory, as well as all classes in JAR files found in the WEB-INF/lib/ directory, are made visible to
other classes within your particular web application. Thus, if you include all of the required library classes in one of these places, you will simplify the installation of your web application - no adjustment to the system class path (or installation of global library files in your server) will be necessary.
Servlet Web Application File Layout
Tomcat Server & Testing Tomcat
Application Deployment Once you have gotten all the files of your web application ready, it is time to deploy the
application on Tomcat. This step can be done in two ways to be explained respectively in the following parts.
Deploying “ Unpacked”
Servlets and Java Server Pages
A web application can be deployed in Tomcat by simply copying the unpacked directory hierarchy into a subdirectory in directory $CATALINA_HOME/webapps/, where $CATALINA_HOME is the directory where Tomcat is installed. As mentioned above, the /WEB-INF/web.xml file contains the Web Application Deployment Descriptor for your application. As the filename extension implies, this file is an XML document, and defines everything about your application that a server needs to know. For servlets or JSPs to be accessible, you must configure the URI to which a servlet or a JSP is mapped by providing a servlet-mapping element in the WEB-INF/web.xml file, for example. Listing the servlet in the descriptor is required if you want to provide an alternate mapping, pass any initialization parameters to the servlet, specify loading order on startup, and so on. The servlet element is an XML tag that appears near the start of web.xml, and it is used for all of these tasks. Here is an example of a servlet with most of its allowed subelements:
Forescore and seven years ago...
Once you have your servlets in place, you may also need to add JSPs to your application. JSPs can be installed anywhere in a web application, except under WEB-INF; this folder is protected against access from the Web, since it can contain initialization parameters such as database connections, names, and passwords. JSPs can be copied to the root of your web application or placed in any subdirectory other than WEB-INF. The same goes for any static content, such as HTML files, data files, and image files.
Deploying Applications in WAR Format
Although you can create directories and copy files using the techniques in the previous section, there are some advantages to using the Web Application Archive packaging format described in the servlet specification. A major benefit with Tomcat is automatic deployment: a single WAR file can be copied directly to Tomcat s webapps directory, and it will be automatically available as a context, without requiring any configuration. ’
Creating WAR files is actually accomplished in the same way you create JAR files: through the jar command. So, assuming you have your web application set up correctly and completely in a directory called testapp, you can do the following:
N. Sridhar, Associate Professor, GMRIT
Generating Dynamic Content JSP is all about generating dynamic content: content that differs based on user input, time of day, the state of an external system, or any other runtime conditions. JSP provides you with lots of tools for generating this content. In this book, you will learn about them all -- standard actions, custom actions, the JSP Standard Tag Library, JavaBeans, and scripting elements. Before going into all of that, however, let's start with a simple example to get a better feel for how the basic JSP elements work.
Creating a JSP Page Recall from Chapter 3 that a JSP page is just a regular HTML page with a few special elements. A JSP page should have the file extension .jsp, which tells the server that the page needs to be processed by the JSP container. Without this clue, the server is unable to distinguish a JSP page from any other type of file and sends it unprocessed to the browser. When working with JSP pages, you just need a regular text editor such as Notepad on Windows or Emacs on Unix. There are a number of tools that may make it easier for you, such as syntax-aware editors that color-code JSP and HTML elements. Some Interactive Development Environments (IDE) even include a small web container that allows you to easily execute and debug the pages during development. There are also several web-page authoring tools -- the type of tools often used when developing regular HTML pages -- that supports JSP to some degree. You can browse through a fairly extensive list of tools like this at my web site: http://TheJSPBook.com/. I recommend that you do not use them initially, though; it's easier to learn how JSP works if you see the raw page elements before you use tools that hide them.
Installing a JSP Page A complete web application may consist of several different resources: JSP pages, servlets, applets, static HTML pages, custom tag libraries, and other Java class files. Until very recently, an application with all these components had to be installed and configured in different ways for different servers, making it hard for web application developers to provide easy-to-use installation instructions and tools. Starting with the Servlet 2.2 specification, there's a standard, portable way to package all web application resources, along with a deployment descriptor. The deployment descriptor is a file named web.xml, containing information about security requirements, how all the resources fit together, and other facts about the application. The deployment descriptor and all the other web application files are placed in a WAR file, arranged in a well-defined hierarchy. A WAR file has a .war file extension and can be created with the Java jar command or a ZIP utility program, such as WinZip (the same file format is used for both JAR and ZIP files). Having a standardized web application format lets container vendors develop installation and configuration tools that make it easy to install an application. During installation, the container is free to unpack the contents of the WAR file and store it for runtime use in any way it sees fit, but the application developer needs to deal with only one delivery format. Even though a container is required to know how to deal only with applications packaged as a WAR file, most (if not all) containers also let you store your application files directly in a
filesystem using the same file structure as is defined for the WAR file. During development, it's more convenient to work with the files in a regular filesystem structure instead of creating an updated WAR file every time you make a change. In Tomcat 4, for instance, any subdirectory under the webapps directory is assumed to be a web application, using the standard web application file structure.
Running a JSP Page Assuming you have installed all book examples as described in Chapter 4, first start the Tomcat server and load the book examples main page by typing the URL http://localhost:8080/ora/index.html in the browser address field. Note how the /ora part of the URL matches the Tomcat webapps subdirectory name for the example application. This part of the URL is called the application's context path; every web application has a unique context path, assigned one way or another when you install the application. Tomcat 4 uses the subdirectory name as the context path by default, but other containers may prompt you for a path in an installation tool or use other conventions. When you make a request for a web application resource (an HTML or JSP page, or a servlet), the first part of the URL (after the hostname and port number) must be the context path, so the container knows which application should handle the request. There's one exception to this rule; one application per container may be installed as the default, or root, application. For Tomcat 4, this application in stored in the webapps/ROOT directory, by default. Requests for resources in the default application don't start with a context path (or more accurately, have an empty string as their context path). For instance, the http://localhost:8080/index.html URL is used to request a page in the default application.
N. Sridhar, Associate Professor, GMRIT, Rajam
Using Scripting Elements JSP Elements There are three types of JSP elements you can use: directive, action, and scripting. Directive elements
The directive elements, shown in table specify information about the page itself that remains the same between requests -- for example, if session tracking is required or not, buffering requirements, and the name of a page that should be used to report errors, if any
Standard action elements
Action elements typically perform some action based on information that is required at the exact time the JSP page is requested by a browser. An action can, for instance, access parameters sent with the request to do a database lookup. It can also dynamically generate HTML, such as a table filled with information retrieved from an external system.
Scripting elements
Scripting elements, shown in Table , allow you to add small pieces of code (typically Java code) in a JSP page, such as an if statement to generate different HTML depending on a certain condition. Like actions, they are also executed when the page is requested. You should
use scripting elements with extreme care: if you embed too much code in your JSP pages, you will end up with the same kind of maintenance problems as with servlets embedding HTML
JSP Directives Besides the fixed template text, the easy.jsp page also produces dynamic content. It has very simple dynamic content -- the sum of 1, 2 and 3 calculated at runtime -- but step back a moment and think about the type of dynamic content you see on the Web every day. Common examples might be a list of web sites matching a search criterion on a search engine site, the content of a shopping cart on an e-commerce site, a personalized news page, or messages in a bulletin board. The actual data for the dynamic content can come from many types of sources, for instance from a database, an XML document, or data accumulated in memory based on previous requests. The dynamic data needs to be combined with regular HTML elements into a page with the right layout, navigation bars, the company logo, and so forth, before it's sent to the browser. When using JSP, the regular HTML is the template text described earlier, and the dynamic data is inserted at the appropriate place in the template text using a JSP action element. A JSP action is executed when a JSP page is requested In other words, JSP action elements represent dynamic actions that take place at runtime, as opposed to JSP directives, which are used only during the translation phase (when the JSP page is turned into Java servlet code). An action can add text to the response, as in the example used in this chapter, but it can also do other things such as write to a file on the server, send an email, or retrieve data from a database that is later added to the response by other actions. Example shows the easy.jsp page again, this time with the JSP action element highlighted. JSP action elements <%@ page con tentType="text/html" %> <%@ taglib pr efix="c" uri="http://java.sun.com/jstl/core" %>
JSP is as easy as ...
<%-- Calculate the sum of 1 + 2 + 3 dynamically --%> 1 + 2 + 3 =Implicit JSP Objects
Error Handling and Debugging When you develop any application that's more than a trivial example, errors are inevitable. A JSP-based application is no exception. There are many types of errors you will deal with. Simple syntax errors in the JSP pages are almost a given during the development phase. And even after you have fixed all the syntax errors, you may still have to figure out why the application doesn't work as you intended because of design mistakes. The application must also be designed to deal with problems that can occur when it's deployed for production use. Users can enter invalid values and try to use the application in ways you never imagined. External systems, such as databases, can fail or become unavailable due to network problems. Since a web application is the face of the company, making sure it behaves well, even when the users misbehave and the world around it falls apart, is extremely important for a positive customer perception. Proper design and testing is the only way to accomplish this goal. In this chapter, we look at the types of problems you can expect during development, as well as those common in a production system. We see how you can track down JSP syntax and design errors and how to deal with runtime problems in a graceful manner.
Dealing with Syntax Errors The first type of error you will encounter is the one you, or your coworkers, create by simple typos; in other words, syntax errors. The JSP container needs every JSP element to be written exactly as it's defined in the specification in order to process the JSP page. When it finds something that's not right, it tells you. How easy it is to understand what it tells you depends on the type of error, the JSP container implementation, and sometimes, on how fluent you are in computer gibberish.
Element Syntax Errors All container implementations report syntax errors, but details such as the wording of the messages, how much information the message contains, and where the message is written differ between them. In this chapter, I show examples only of the messages produced by Tomcat.
Improperly terminated action (error2.jsp) <%@ page con tentType="text/html" %> <%@ taglib pr efix="c" uri="http://java.sun.com/jstl/core" %>
JSP is as easy as ...
1 + 2 + 3 =The syntax error here is almost the same as the "unterminated tag" in Example 9-1, but now it's the
required for an empty element). The message reported by Tomcat in this case is: End of content reached while more parsing required: tag n esting error?
This message isn't really helpful, because the line and position information is missing, and it gives no clue about which action element is in error. The error in this case is that, since the action element doesn't have a body, a single tag ending with /> should be used, but in Example 2 it's terminated with just >. Because that's valid syntax for a JSP action that contains a body, the JSP container can't tell that it's a syntax error at this point. Instead, it treats it as the opening tag for an element with a body and complains that it can't find the closing tag before the file ends. The error message could be a lot more informative, for instance include the name of the action element that is missing the closing tag, and maybe even the position of the opening tag. Let's hope this is fixed in the Tomcat version you use.
Expression Language Syntax Errors How well JSTL EL syntax errors are reported varies between JSTL implementations and web containers, because the EL isn't yet part of the JSP specification. The JSTL Reference Implementation (RI) is doing a pretty good of job of reporting EL syntax errors. In a web container that implements an optional JSP it even includes information about the exact line and column in the JSP source file. Unfortunately, Tomcat 4 doesn't implement this feature yet, but will hopefully do so in a future version. We look at a few EL syntax error examples in this section so you can see what to expect when you use the JSTL RI and Tomcat 4.0.4. Later versions, and other implementations may do better (or worse), but these examples illustrate what to look for.
N.Sridhar, Associate Professor, IT, GMRIT
Sharing Data Between JSP Pages, Requests, and Users Any real application consists of more than a single page, and multiple pages often need access to the same information and server-side resources. When multiple pages process the same request (e.g., one page that retrieves the data the user asked for and another that displays it), there must be a way to pass data from one page to another. In an application in which the user is asked to provide information in multiple steps, such as an online shopping application, there must be a way to collect the information received with each request and get access to the complete set when the user is ready. Other information and resources need to be shared among multiple pages, requests, and all users. Examples are information about currently logged-in users, database connection pool objects, and cache objects to avoid frequent database lookups. In this chapter you will learn how scopes in JSP provide access to this type of shared data. You will also see how using multiple pages to process a request leads to an application that's easier to maintain and expand, and learn about a JSP action that lets you pass control between the different pages.
The different aspects of the User Info example can be categorized like this: Display the form for user input (presentation) Validate the input (request processing and business logic) Display the result of the validation (presentation) •
•
•
Passing Control and Data Between Pages
One of the most fundamental features of JSP technology is that it allows for separation of request processing, business logic and presentation, using what's known as the Model-View-Controller (MVC) model.
As you may recall, the roles of Model, View, and Controller can be assigned to different types of server-side components. In this part of the book, JSP pages are used for both the Controller and View roles, and the Model role is played by either a bean or a JSP page. This isn't necessarily the best approach, but it lets us focus on JSP features instead of getting into Java programming. If you're a programmer and interested in other role assignments, you may want to take a peek at Chapter 17 and Chapter 18. These chapters describe other alternatives and focus on using a servlet as the Controller. In this section we look at how to separate the different aspects in a pure JSP application, using a modified version of the User Info example from Chapter 8 as a concrete example. In this application, the business logic piece is trivial. However, it sets the stage for a more advanced application example in the next section and the remaining chapters in this part of the book; all of them use the pattern introduced here. The different aspects of the User Info example can be categorized like this: 1. Display the form for user input (presentation) 2. Validate the input (request processing and business logic) 3. Display the result of the validation (presentation)
Passing Control from One Page to Another Before digging into the modified example pages, let's go through the basic mechanisms for satisfying the two requirements. As shown in Figure 10-1, the userinfovalidate.jsp page passes control to one of two other pages based on the result of the input validation. JSP supports this through the
The
Parameters specified with
Passing Data from One Page to Another JSP provides different scopes for sharing data objects between pages, requests, and users. The scope defines how long the object is available and whether it's available only to one user or to all application users. The following scopes are defined: page, request , session, and application. Objects placed in the default scope, the page scope, are available only within that page. That's the scope used in all examples you have seen so far. The request scope is for objects that need to be available to all pages processing the same request. Objects in the session scope are available to all requests made from the same browser, and objects in the application scope are shared by all users of the application (see Figure 10-2). According to the JSP specification, the name used for an object must be unique within all scopes. This means that if you have an object named userInfo in the application scope, for instance, and save another object with the same name in the request scope, the container may remove the first object. Few containers (if any) enforce this rule, but you should ensure you use unique names anyway to avoid portability problems
N.Sridhar, Associate Professor, GMRIT, Rajam
Sharing Session and Application Data The request scope makes data available to multiple pages processing the same request. But in many cases, data must be shared over multiple requests. Imagine a travel agency application. It's important to remember the dates and destination entered to book the flight so that the customer doesn't have to reenter the information when it's time to make hotel and rental car reservations. This type of information, available only to requests from the same user, can be shared through the session scope. Some information is needed by multiple pages independent of who the current user is. JSP supports access to this type of shared information through the application scope. Information saved in the application scope by one page can later be accessed by another page, even if the two pages were requested by different users. Examples of information typically shared through the application scope are database connection pool objects, information about currently logged-in users, and cache objects that avoid unnecessary database queries for data that is the same for all users.
The upcoming examples in this chapter will help you to use the session and application scopes.
Session Tracking Explained Keeping track of which requests come from the same user isn't as easy as it may look. As described in Chapter 2, HTTP is a stateless, request-response protocol. What this means is that the browser sends a request for a web resource; the web server processes the request and returns a response. The server then forgets this transaction ever happened. So when the same browser sends a new request; the web server has no idea that this request is related to the previous one. This is fine as long as you're dealing with static files, but it's a problem in an interactive web application. There are two ways to solve this problem, and they have both been used extensively for web applications with a variety of server-side technologies. The server can either return all information related to the current user (the client state) with each response and let the browser send it back as part of the next request, or it can save the state somewhere on the server and
send back only an identifier that the browser returns with the next request. The identifier is then used to locate the state information saved on the server. In both cases, the information can be sent to the browser in one of three ways: As a cookie Embedded as hidden fields in an HTML form Encoded in the URLs in the response body, typically as links to other application pages (this is known as URL rewriting) •
•
•
A cookie is a name/value pair that the server passes to the browser in a response header. The
browser stores the cookie for the time specified by the cookie's expiration time attribute. When the browser sends a request to a server, it checks its "cookie jar" and includes all cookies it has received from the same server (that have not yet expired) in the request headers. Cookies used for state management don't have an expiration time and expire as soon as the user closes the browser. Using cookies is the easiest way to deal with the state issue, but some browsers don't support cookies. In addition, a user may disable cookies in a browser that does support them because of privacy concerns. Hence, we can't rely on cookies alone
If hidden fields in an HTML form are used to send the state information to the browser, the browser returns the information to the server as regular HTTP parameters when the form is submitted. When the state information is encoded in URLs, it's returned to the server as part of the request URL path, for instance when the user clicks on an encoded link. Sending all state information back and forth between the browser and server isn't efficient, so most modern server-side technologies keep the information on the server and pass only an identifier between the browser and the server. This is called session tracking; all requests from a browser that contains the same identifier (session ID) belong to the same session, and the server keeps track of all information associated with the session. JSP hides all details of cookie-based session tracking and supports the URL rewriting variety with a bit of help from the page author. In addition, the specification allows a container to use the session mechanism built into the Secure Socket Layer (SSL), the encryption technology used by HTTPS. SSL-based session tracking is currently not supported by any of the major servlet containers, but all of them support the cookie and URL rewriting techniques. No matter which mechanism is used, session data is always available to JSP pages through the session scope.2 Information saved in the session scope is available to all pages requested by the same browser during the lifetime of a session. A session starts when the browser makes the first request for a JSP page in a particular application. The application can explicitly end the session (for instance when the user logs out or completes a transaction), or the JSP container can end it after a period of user inactivity (the default value is typically 30 minutes after the last request). Note that there's no way for the server to tell if the user closes the browser, because there's no permanent connection between the browser and the server, and no message is sent to the server when the browser disappears. Still, closing the browser usually means losing the session ID; the cookie expires, or the encoded URLs are no longer available. So when the user opens a browser again, the server can't associate the new request with the previous session, and therefore creates a new session. However, all session data associated with the previous session remains on the server until the session times out.
N.Sridhar, Associate Professor, GMRIT, Rajam
Database Access Strategies We take a closer look at the strategies for using a database in a web application that I've mentioned in the previous chapters. In case you're new to Java database access, we start with a brief overview of the most important JDBC classes and interfaces. Next, we focus in on the JDBC Connection class and how pooling Connection objects helps to solve a number of common problems. We look at two ways to implement connection-pooling capabilities: the JDBC 2.0 way and by letting a JDBC 1.0 connection pool simulate a JDBC 2.0 pool. A connection pool can be made available to the rest of the application -- servlets as well as the JSTL database access actions -- in a number of ways. In this chapter we discuss the approach used in Chapter 18 (using an application event listener) in more detail, as well as an approach that's more flexible but that only works in web containers that support the Java Naming and Directory Interface (JNDI). No matter if you use a servlet or a custom action to access the database, there are a number of JDBC details that must be handled. To help with this grunt work, we look at a generic database access bean that simplifies life and makes the result of a query easy to use. The last section contains an example of an application-specific custom action using this bean.
JDBC Basics The JDBC API is a set of classes and interfaces that allows a Java application to send SQL statements to a database in a vendor-independent way. The API consists mostly of interfaces that define the methods you use in your program. Database engine vendors and third parties provide implementations of these interfaces for a specific database engine; such an implementation is called a JDBC driver . This allows you to develop your program in a database-independent way and connect to a specific database engine by plugging in the appropriate JDBC driver at deployment time. There are JDBC drivers for most database engines on the market, both commercial and open source. If you can't get one from your vendor, check out Sun's list of third-party drivers at http://industry.java.sun.com/products/jdbc/drivers .
All JDBC core classes and interfaces belong to the java.sql package. Of the types shown in Figure 23-1, only the DriverManager is a class (part of the standard J2SE package); the rest are interfaces implemented by each unique JDBC driver. The Driver implementation is the entry point to all the other interface implementations. When the Driver is loaded, it register itself with the DriverManager. When the JDBC application needs a connection to a database, it asks the DriverManager for one, and the DriverManager asks each registered Driver if it knows how to create connections for the requested database. When a Driver replies "yes", the DriverManager asks it for a Connection on the application's behalf; the Driver attempts to create one and return it to the application. The Connection is another core JDBC type. Through the Connection instance, the JDBC application can create Statement instances of different types. The main Statement type can execute a plain SQL statement, such as SELECT, UPDATE, or DELETE. When a SELECT statement is executed, the result is returned as an instance of ResultSet. The ResultSet has methods for navigating the result rows and asking for the column values in
the current row. There are two specialized Statement types: PreparedStatement and CallableStatement. For a PreparedStatement, you can specify an SQL statement where, instead of literal column values, the statement contains parameter placeholders, symbolized by question marks: SELECT * FROM Enployee WHERE UserName = ?
Special setter methods assign values to the placeholders before the SQL statement is executed. The same PreparedStatement can then be assigned new placeholder values and executed again. This allows a database to parse the statement once, typically caching a strategy for how to execute it in the most efficient way, and then execute it over and over again with new values. This can result in dramatically improved performance over using a
regular Statement. The PreparedStatement is also useful in other ways, as we will discuss later. The CallableStatement is for stored procedures. The same as for a PreparedStatement, you can assign values to input arguments, but in addition, there are methods for declaring the types of output arguments. Other interfaces in the JDBC API provide access to metadata about the database and the JDBC driver itself ( DatabaseMetaData, available from the Connection, containing information about supported features) as well as about a ResultSet (ResultSetMetaData, available from the ResultSet, containing information about column data types, null values, etc.). To see how it all fits together, here's a simple program that uses most of these classes: import java.sql.*; public class DBTest { public static void main(String[] args) throws Exception { // Load the JDBC Driver Class.forName("oracle.jdbc.OracleDriver"); // Get a Connection String url = "jdbc:oracle:thin:@myhost:1521:ORASID"; Connection conn = DriverManager.getConnection(url, "scott", "tiger"); ResultSet rs = null; PreparedStatement pstmt = null; String sql = "SELECT * From Employee WHERE UserName = ?"; try { pstmt = conn.prepareStatement(sql); pstmt.setString(1, "hans"); rs = pstmt.executeQuery( ); while (rs.next( )) { System.out.println(rs.getString("FirstName")); System.out.println(rs.getString("LastName")); } } finally {
if (rs != null) { try {rs.close( );} catch (SQLException e) {} } if (pstmt != null) { try {pstmt.close( );} catch (SQLException e) {} } if (conn != null) { try {conn.close( );} catch (SQLException e) {} } } } } It first loads a Driver (an Oracle JDBC driver in this example) and then gets a Connection. The getConnection( ) argument is a JDBC URL that identifies a specific database. Different JDBC drivers use different URL syntax. All JDBC URLs starts with jdbc: followed by a JDBC driver identifier, such as oracle: for Oracle's drivers. The rest of the URL is used to identify other details for the driver and database instance.
N.Sridhar, Associate Professor, GMRIT, Rajam
Hierarchy of JDBC Studying Javax.sql.* package
Accessing a Database from a JSP Page
Using Connections and Connection Pools In a JDBC-based application, a lot revolves around the java.sql.Connection interface. Before any database operations can take place, the application must create a Connection to the database. It then acts as the communication channel between the application and the database, carrying the SQL statements sent by the application and the results returned by the database. A Connection is associated with a database user account to allow the database to enforce access control rules for the SQL statements submitted through the Connection. Finally, the Connection is also the boundary for database transactions. Only SQL statements executed through the same Connection can make up a transaction. A transaction consists of a number of SQL statements that must either all succeed or all fail as one atomic operation. A transaction can be committed (the changes resulting from the statements are permanently saved) or rolled back (all changes are ignored) by calling Connection methods. In a standalone application, a Connection is typically created once and kept open until the application is shut down. This isn't surprising, since a standalone application serves only one user at a time, and all database operations initiated by a single user are typically related to each other. In a server application that deals with unrelated requests from many different users, it's not so obvious how to deal with connections. There are three things to consider: a Connection is time-consuming to create, it must be used for only one user at a time to avoid transaction clashes, and it's expensive to keep open. Creating a Connection is an operation that can actually take a second or two to perform. Besides establishing a network connection to the database, the database engine must authenticate the user and create a context with various data structures to keep track of transactions, cached statements, results, and so forth. Creating a new Connection for each request received by the server, while simple to implement, is far too time-consuming in a high-traffic server application.
Bean Model for JDBC import java.util.*; import java.sql.*; import javax.servlet.jsp.jstl.sql.*; public class SQLCommandBean { private Connection conn; private String sqlValue; private List values; public void setConnection(Connection conn) { this.conn = conn; } public void setSqlValue(String sqlValue) { this.sqlValue = sqlValue; } public void setValues(List values) { this.values = values; }
...
Specific Database Actions
Developing Application-Specific Database Components The SQLCommandBean class described in this chapter can be used for application specific components that access a database. The bean is used like this: SQLCommandBean sqlCommandBean = new SQLCommandBean( ); sqlCommandBean.setConnection(dataSource.getConnection( )); String sql = "SELECT * FROM Employee WHERE UserName = ?"); sqlCommandBean.setSqlValue(sql); List values = new ArrayList( ); values.add(userName); sqlCommandBean.setValues(values); Result result = sqlCommandBean.executeQuery( );
You can also use these classes in your application-specific custom actions. One example is the custom action that's mentioned in previous as an alternative to the generic database actions for inserting or updating employee information:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %> <%@ taglib prefix="myLib" uri="/mytaglib" %>
Using JavaBeans Components in JSP Pages The JavaBeans specification defines a set of programming conventions for Java classes that should be used as pluggable components. In layman's terms, tools that have no inside information about a class can use it if it's developed according to these conventions. For instance, a GUI builder tool can support widgets developed as JavaBeans components. A JavaBeans component, or just a bean for short, is often used in JSP as the container for the dynamic content to be displayed by a web page. It typically represents something specific, such as a person, a product, or a shopping order. When JSP is combined with servlets, the bean can be created and initialized with data by the servlet and passed to a JSP page that simply adds the bean's data to the response. But even in a pure JSP application, a bean is a useful tool, for instance for capturing and validating user input. A programmer must develop the bean, but someone who doesn't have any programming experience can then use it in a JSP page. JSP defines a number of standard actions for working with beans, and the JSTL Expression Language accepts beans as variables in expressions. In this chapter, we take a closer look at what a bean is and how it can produce dynamic content in a page. We'll return to beans in Chapter 8 to see how they can be used for input validation.
What Is a Bean? As I said earlier, a bean is simply a Java class that follows certain coding conventions, so it can be used by tools as a component in a larger application. It can be instantiated and made available to the JSP page in a couple of ways. In an application that uses a servlet as a frontend for all business logic, the bean is typically created by the business logic code and sent to the JSP page to include its contents in the response. Data held by a bean is referred to as the bean's properties. The property name is case-sensitive and always starts with a lowercase letter. A property is either read-only, write-only or read/write, and has a value corresponding to a specific Java data type (for instance String, java.util.Date, or int). Properties can be read and set through the bean's s accessor methods, which are regular Java methods named according to the JavaBeans conventions. What you need to know to use a bean in a JSP page is its class name, the property names, the property data types, the property access types, and a description of the data represented by each property. You don't have to worry too much about the data type, since the JSP elements used to get and set properties typically handles the conversion between regular string values and the real Java type transparently
A page using a bean (cartoon.jsp)
A dose of Dilbert
Connection Pooling with JSP-JDBC Using a JDBC 2.0 Optional Package Connection Pool Connection pools exist in many forms. You find them in books, articles, and on the Web. Yet prior to JDBC 2.0, there was no standard defined for how a Java application would interact with a connection pool. The JDBC 2.0 Optional Package (formally known as a Standard Extension), now part of JDBC 3.0 and included in the Java SDK 1.4, changed this by introducing a set of interfaces that connection pools should implement: javax.sql.DataSource
A DataSource represents a database. This is the interface the application always uses to get a Connection. The class that implements the interface can provide connection-pooling capabilities or hand out regular, unpooled Connection objects; the application code is identical for both cases, as described later. javax.sql.ConnectionPoolDataSource
A DataSource implementation that provides pooling capabilities uses a class that implements the ConnectionPoolDataSource interface. A ConnectionPoolDataSource is a factory for PooledConnection objects. The application code never calls methods in this interface directly. javax.sql.PooledConnection
The objects a DataSource with pooling capabilities keeps in its pool implement the PooledConnection interface. When the application asks the DataSource for a Connection, it locates an available PooledConnection object or gets a new one from its ConnectionPoolDataSource if the pool is empty. The PooledConnection provides a getConnection( ) method that returns a Connection object. The DataSource calls this method and returns the Connection to the application. This Connection object behaves like a regular Connection with one exception: when the application calls the close( ) method, instead of closing the connection to the database, it informs the PooledConnection it belongs to that it's no longer used. The PooledConnection relays this information to the DataSource, which returns the PooledConnection to the pool. how an application uses implementations of these interfaces to obtain a pooled connection and how to return it to the pool.
N.Sridhar, Associate Professor, GMRIT, Rajam
&.':/9!% 6+,3%0,>65;9633,9%0:(+,:0.57(;;,957<;;6.,;/,9;6/,37*65;963*/(5., %+,*6<73,:05;,9-(*,-964)<:05,::36.0*(5++(;( !5*+2 #/,46+,3*65;(05:;/,*69,6-;/,(7730*(;065:-<5*;065(30;@#/,46+,3 ,5*(7:<3(;,:;/,:;(;,6-;/,(7730*(;065"64,;04,:;/,653@-<5*;065(30;@0;*65;(05:0: :;(;,;256>:56;/05.()6<;;/,=0,>69*65;9633,9 %/+= #/,=0,>796=0+,:;/,79,:,5;(;0656-;/,46+,3;0:;/, 6-;/,(7730*(;065 #/,=0,>*(5(**,::;/,46+,3.,;;,9:)<;0;/(:56256>3,+.,6-;/,:,;;,9:5 (++0;0650;256>:56;/05.()6<;;/,*65;9633,9#/,=0,>:/6<3+),56;0-0,+>/,5 */(5.,:;6;/,46+,36**<9 54:8522+8#/,*65;9633,99,(*;:;6;/,<:,9057<;; *9,(;,:(5+:,;:;/,46+,3 &.':/9',8'3+=581 -9(4,>6920:4(+,<76-;/,:,;6-*3(::,:>/0*/(336><:;6<:,(30)9(9@05(),:; 76::0)3,>(@-69(:7,*0-0*9,8<09,4,5; &.':/9$:8;:9,8'3+=581 ";9<;:-9(4,>6920:(567,5:6<9*,-9(4,>692-69+,=,36705.;/,>,)(7730*(;065:05 (=()(:,+65%(9*/0;,*;<9,;<:,:(5+,?;,5+:;/,(=(",9=3,;";9<;: 0:96)<:;(9*/0;,*;<9,(5+*(5),<:,+-69;/,+,=,3674,5;6-(7730*(;0656-(5@:0A, ";9<;:-9(4,>6924(2,:0;4<*/,(:0,9;6+,:0.5:*(3()3,9,30()3,&,)(7730*(;065:>0;/ (=( &.':'8+:.+)58+)2'99+95,:.+$:8;:98'3+=581 ";9<;:0:(:,;6-*667,9(;05.*3(::,::,9=3,;:(5+";(.:;/(;4(2,<7(9,<:()3, %+,:0.5 (=(,(5:*64765,5;:-694(5(.05.(7730*(;065:;(;,(5+),/(=069 =,5;+90=,5+,=,3674,5;=0(30:;,5,9:(:05;9(+0;065(3$+,=,3674,5; (.,:;/(;9,79,:,5;%:;@3,=0,>:7(.,:9,-,9,5*,=0,>966;:=0(;/," *64765,5;;9,, &.':/9):/54$+8<2+: *;065",9=3,;0:(:0473,:,9=3,;>/0*/0:;/,)(*2)65,6-(33";9<;:(7730*(;065:;0:;/, 4(0565;9633,9*64765,5;;/(;/(5+3,:*30,5;9,8<,:;:(5++,;,9405,:>/0*/*;065>033 796*,::,(*/9,*,0=,+9,8<,:;;:,9=,:(:(5*;065-(*;69@E*9,(;05.:7,*0-0**;065 *3(::,:)(:,+65<:,9F:9,8<,:; &.':/9852+5,):/54$+8<2+: *;065",9=3,;7,9-694:;/,963,6-65;9633,9 96*,::<:,99,8<,:;: ,;,9405,>/(;;/,<:,90:;9@05.;6(*/0,=,(**69+05.;6;/,9,8<,:; <33+(;(-964;/,46+,30-5,*,::(9@;6),.0=,5;6;/,(7796790(;,=0,> ",3,*;;/,7967,9=0,>;69,:765+;6;/,<:,9 ,3,.(;,:46:;6-;/0:.9<5;>692;6*;065*3(::,: :9,:765:0)3,-69050;0(30A(;065(5+*3,(5<76-9,:6<9*,: &.':/9:.+):/54583
*;0656940:1(=(),(5>/0*/9,79,:,5;:;/,-694057<;:*65;(0505.;/,9,8<,:; 7(9(4,;,9:-964;/,%0,>9,-,9,5*05.;/,*;065),(5 &.':'8+:.+/3658:'4:3+:.5*95,):/54583 #/,04769;(5;4,;/6+:6-*;065694(9, =(30+(;, 9,:,; +9)8/(+<'2/*':+'4*8+9+:3+:.5*9 $:,+;6=(30+(;,7967,9;0,:(-;,9;/,@/(=,),,5767<3(;,+(33,+),-69, 694,(50:/(5+,+;6*;065!,;<95:(*633,*;0656- *;0659969 (: *;0659969: 6336>05.0:;/,4,;/6+:0.5(;<9,-69;/, =(30+(;, 4,;/6+ 6;(2/) *;0659969:=(30+(;,*;065(7705. 4(7705.;;7",9=3,;!,8<,:; 9,8<,:;
9,:,;4,;/6+0:*(33,+)@";9<;:9(4,>692>0;/,(*/9,8<,:;;/(;<:,:;/, +,-05,+*;065694#/,7<976:,6-;/0:4,;/6+0:;69,:,;(336-;/,*;065694:+(;( 4,4),9:79069;6;/,5,>9,8<,:;=(3<,:),05.:,;
7<)30*=60+ 9,:,;BC &.':/9):/54!'66/4- *;0654(7705.*65;(05:(33;/,+,736@4,5;05-694(;065-69(7(9;0*<3(9*;065),(5#/0: *3(::0:;6+,;,9405,>/,9,;/,9,:<3;:6-;/,*;065>033),:,5;65*,0;:796*,::05.0: *6473,;, 5=/9:.+):/54!'66/4-96+)/,/+* &,*(5:7,*0-@;/,(*;0654(7705.05;/,*65-0.<9(;065-03,*(33,+ :;9<;:*65-0.?43 ";9<;:-9(4,>692*9,(;,: *;065(7705. 6)1,*;-964 *;065(7705. *65-0.<9(;065 ,3,4,5;6- :;9<;:*65-0.?43 -03, (*;0654(7705.: (*;065 7(;/:<)40; ;@7,:<)40;"<)40;*;065 5(4,:<)40;694 057<;:<)40;1:7 :*67,9,8<,:; =(30+(;,;9<, -69>(9+ 5(4,:<**,:: 7(;/:<**,::1:7 -69>(9+ 5(4,-(03<9, 7(;/,99691:7 (*;065 (*;0654(7705.: &.':/9852+5,):/542'99 5*;0653(::7,9-694:(963,6-(5(+(7;,9),;>,,5;/,*65;,5;:6-(505*6405.## 9,8<,:;(5+;/,*699,:765+05.)<:05,::36.0*;/(;:/6<3+),,?,*<;,+;6796*,::;/0: 9,8<,:; 4=./).3+:.5*5,):/54)2'99:.+(;9/4+9925-/)/9+>+);:+*