Learning Jakarta Struts 1.2 A concise and practical tutorial Stephan Wiesner
BIRMINGHAM - MUMBAI
Learning Jakarta Struts 1.2 A concise and practical tutorial Copyright © 2005 Packt Publishing All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews. Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the authors, Packt Publishing, nor its dealers or distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book. Packt Publishing has endeavored to provide trademark information about all the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information. First published: August 2005. Published by Packt Publishing Ltd. 32 Lincoln Road Olton Birmingham, B27 6PA, UK. ISBN 1-904811-54-X www.packtpub.com
Cover Design by www.visionwt.com Authorized translation from the German Edition: "Struts: Tutorial für Java-Entwickler" © 2004 by Galileo Press GALILEO COMPUTING is an imprint of Galileo Press, Fort Lee, NJ (USA), Bonn (Germany). German Edition first published 2004 by Galileo Press. FM-2
Credits Author Stephan Wiesner
Translator Suranga Ketkar
Technical Editor Richard Deeson
Proofreader Chris Smith
Illustrator Dinesh Kandalgaonkar
Layout Nanda Padmanabhan
Cover Designer Helen Wood
FM-3
Preface It's a long journey via instructions, Short and effective via examples. Seneca This book offers step-by-step instructions on the Jakarta Struts Framework as well as on building a web application based on this Open Source software. As the book progresses, we will develop a web shop: the classic book store. This web application will help us to integrate the individual components of Struts into a complete and extensive package. The crucial point lies in the practical application and not so much in the theoretical background. This book will first demonstrate the process of developing an application accompanied by short explanations. You can then experiment with the given code.
Struts is Simple Struts, just like Java, is fundamentally simple. Any technology becomes simple when you understand it well. You must properly solve a problem in order to thoroughly understand it. Then you examine your old code, change it a little bit, apply few classes and you'll be glad how fast you make progress. It's difficult only the first time—something that holds true for most things even outside computer world. Time and again we encounter the following obstacles: • • •
The same problem rarely occurs twice. One must learn constantly. New things are usually poorly documented.
All these reasons lead to frustration and prejudices people against trying out new things. This book offers an entry point into Struts; it cannot possibly cover everything. To make learning this new technology easier, this book consists of numerous examples and code. It follows the "Learning by doing" philosophy and I sincerely hope that you will find it useful and engaging.
What You Should Know Beforehand The intended audience for this book is students with some computer science knowledge and interested Java developers who want to develop a small web project. The goal is to impart as much practice-oriented knowledge as possible. This tutorial is not aimed for theoretical training and does not claim to be a complete reference book. The book expects a certain basic familiarity with Java, JSP and servlets (uncommon elements will, of course, be explained). JSPs and servlets are Java classes and it does not make sense to try to understand them without the required knowledge as a foundation. This book is for anyone who is looking for an entry-level book to start working with Struts. Therefore no previous knowledge of Struts is required. It is assumed that you have a PC at your disposal and you are allowed to set up and operate a server on it. A connection to the Internet is not required. If you are using a PC at a university or office, please make sure you have the rights to install software and operate a server (eventually you'll have to use other ports).
Operating System and Tools Windows XP is the operating system used in this book. Users of different systems must revise path names to some extent but other than that, most key points are operating system independent. However, Linux/UNIX users must forgive me if I don't always differentiate strictly between upper and lower case! The browsers used are MS Explorer version 6.x and Mozilla 1.4. The software has been developed with www.jedit.org and www.eclipse.org. I describe installation processes for any required software. All references to file paths use the defaults for these installations. If you want to use other paths, then you have to revise them accordingly.
Software Versions The following software versions are used: • • • •
JDK 1.4.2: http://java.sun.com/j2se MySQL 4.1: http://www.mysql.com Struts 1.2: http://jakarta.apache.org/struts Tomcat 4.1.24: http://jakarta.apache.org/tomcat/. (The shop has also been tested on versions 4.1.27 and 5.0.14.)
Code The code examples aim to promote understanding and help to better explain the facts. Complete code listings as well as code fragments are given. At some places, only completed versions of code are given in the book and intermediate steps have been omitted. Full code listings are available on the book's web site. The individual context (see Glossary) for Tomcat is called ShopX, where X is a sequential number. This means that you can follow the development of the application and override your older versions. There are hints in the text for applying new contexts. The code should always be well commented. I haven't included many comments in this book because the accompanied text has detailed documentation. In many places, I purposely employ a less-than-ideal approach. Instead of giving a long explanation of why one solution works better than the other, I simply demonstrate it: You will come across sudden problems in the text later, which could have been avoided early on. In keeping with the saying, "A person learns through mistakes", one must rewrite the code. The main drawback of this approach is that you might consider this book as a reference book. But in my opinion, you will be in a position to solve the problems by referring to the API yourself after you are done with the book, plus you can always look up certain points in the index from time to time. The language files (see Chapter 4, Internationalization and Custom Actions) become more comprehensive as the book progresses. Their content is explicitly explained only in the first lesson. Then you can copy the versions from the big Shop-version or let it grow bit by bit. When an entry fails, Struts displays corresponding error messages. Please make yourself familiar with these messages as early on as possible. The most frequently asked questions on the Struts mailing list are related to these error messages. I have developed the majority of the code examples myself, while other developers have come up with the rest of the examples. In such cases, the original source will be named. In the first instance, you can use the code without any restrictions. In the second case, the rights of the owner of the code may apply. Please confirm how far you are allowed to use any code before you use it. You can download the software that accompanies this book from the Packt website as explained in the Downloading the Example Code for the Book section in the Introduction. It should help you, should you get stuck on a particular problem. Copy and paste has its own essential humble learning effects, but that does not mean that you should not try to solve problems by yourself first!
Exercises This book contains a huge number of exercises. Most of them are fairly small, and can be solved in a few minutes. They help you to absorb the material better and should encourage you to think and scrutinize a little more deeply. You will be doing yourself a great favor if you try to solve the exercises all by yourself. For some exercises, solutions are given in the appendix. Most of them are simply "Test yourself" exercises, because it does not make sense to present all the solutions to you on a silver platter. You should have fun while developing the software and testing it, so play with it a little bit!
Struts as Magic Bullet I was not directly involved in development of Struts and do not have any personal or financial interest in Struts 'taking over the world'. I regard it as a meaningful framework for developing extensive applications, but there are a few points where I would prefer different mediums and techniques. That is why I compare many different methods without favoring Struts, so that you can think over what the best solution is and form your own opinion. It is true that no one technology represents the best solution for all applications.
Table of Contents About the Book
1
Chapter 1: Introduction to Struts
5
1.1 The Apache Jakarta Project
5
1.2 The Struts Project
6
1.3 The Struts Framework
7
1.4 Model Classes
8
1.5 Design Patterns
10
1.6 The Model View Controller (MVC)
10
Exercise 1
1.7 MVC versus Model 2 Exercise 2
11
11 13
1.8 Controller Component: ActionServlet
13
1.9 Design Guidelines for Action Classes
13
1.10 Reflection, Introspection, and DynaBeans
14
1.11 Session
15
1.12 Business Logic
17
1.13 Threads
17
Exercise 3
18
1.14 Expandability
18
1.15 Debugging Struts Applications
18
1.16 Some Experiences
18
1.16.1 Struts in Universities and Colleges
19
1.16.2 Struts in the Enterprise
19
1.16.3 Struts from a Tester's Point of View
19
1.16.4 Struts in Practical Projects
20
Developer 1: Björn
20
Table of Contents
Developer 2: Thorsten
Summary
Chapter 2: Hello Struts
21
23
2.1 Tomcat Server Installation
23
2.2 Installation of Struts
26
2.3 A Simple Page
27
2.4 Internationalization with Struts
28
Exercise 1
Summary
Chapter 3: The Struts Shop
30
30
31
3.1 Why a Bookstore
31
3.2 Iterations
31
3.3 Elements of the Shop
32
Exercise 1
34
3.4 Installing MySQL
34
3.5 Tables
35
Exercise 2
Summary
Chapter 4: Internationalization and Taglibs
36
37
39
4.1 The Structure for the Shop
39
4.2 Internationalization
39
4.2.1 Internationalization and Java
39
4.2.2 Internationalization and Struts
40
4.3 The Welcome Page for the Shop Exercise 1
42 43
4.4 Custom Actions
44
4.5 struts-bean.tld
48
Exercise 2
Summary ii
20
49
49
Table of Contents
Chapter 5: Logging and Configuration
51
5.1 Configuration
51
Exercise 1
53
5.2 Simple Logging
54
5.3 Logging Solutions
54
Exercise 2
55
5.4 JDK Logging
55
Exercise 3
56
5.5 Reading Configuration Settings
57
5.6 Logging with Log4J
59
Exercise 4
60
5.7 Jakarta Commons Logging
61
5.8 Deciding What to Log
61
Exercise 5
Summary
61
62
Chapter 6: Forms
63
6.1 Preparation
63
Exercise 1
6.2 Struts Form Beans
69
70
6.2.1 The Author Form
70
6.2.2 Formatting the Error Message Display
70
6.3 Database
71
6.3.1 Database and JDBC
71
6.3.2 Using Databases in Struts
75
Exercise 2
Summary
Chapter 7: Logic
78
78
81
7.1 Value Comparison
81
7.2 Substring Matching
83
7.3 Redirection
83 iii
Table of Contents
7.4 Collection Utilities
84
7.5 Sorting Tags
85
Exercise 1
89
Summary
Chapter 8: Exceptions
89
91
8.1 Tomcat Error Pages
91
8.2 Custom Exceptions
92
8.3 Exception Handling
92
8.4 ActionErrors
97
Exercise 1
98
Summary
98
Chapter 9: Controller and Templates
99
9.1 Consistent Layout without Struts
99
9.1.1 Why Central Control? Exercise 1
9.2 Central Controller with Struts
103
9.3 Consistent Layout with Struts Tiles
104
9.3.1 Header, Main, and Footer
104
9.3.2 Definitions in a JSP
106
Exercise 2
Summary
Chapter 10: Putting It All Together 10.1 Authorization 10.1.1 Authorization for Tomcat Exercise 1
109
110
111 111 111 113
10.1.2 An Excursion into Session
113
10.1.3 Customers with Application-level Authorization
116
10.2 BookCatalog
120
Exercise 2
124
10.3 Price Information iv
99 102
125
Table of Contents
Exercise 3
10.4 The Shopping Cart 10.4.1 Login Exercise 4
10.5 Using the Shopping Cart Exercise 5
10.6 Placing Orders Exercise 6
Summary
126
126 127 129
129 130
132 132
133
Chapter 11: Struts Validator and Plug-In Classes
135
11.1 Verifying User Input with the Struts Validator
135
Exercise 1
11.1.1 JavaScript JavaScript with the Validator
11.2 Plug-In Classes Exercise 2
Summary
137
139 140
141 142
142
Chapter 12: JSTL
143
12.1 Installation
144
12.2 Internationalization
145
Exercise 1
12.3 Java Classes Exercise 2
12.4 Iteration Exercise 3
146
146 147
147 148
12.5 JSTL and Struts-EL
148
Summary
152
Chapter 13: Tools and Tricks
153
13.1 Code Conventions
153
13.2 The Struts Console
153 v
Table of Contents
Exercise 1
154
13.3 Struts in WSAD (Eclipse)
154
13.4 StrutsDoc and Ant
155
13.4.1 Installing Ant
155
13.4.2 Java API Generation
155
13.4.3 StrutsDoc
158
13.5 Display Tags
159
13.6 Workflow
159
13.7 Testing: HTTPUnit
159
13.8 Different Struts Functions
163
13.8.1 Switching Languages
163
Exercise 2 Exercise 3
164 166
13.8.2 Uploading The Files
166
13.8.3 Precompilation
169
Exercise 4
13.8.4 Validation of struts-config.xml and web.xml 13.9 XSLT Configuration Overview 13.9.1 struts-config.xml Exercise 5
13.9.2 web.xml
172
172 173 174 176
176
13.11 Extending ActionServlet
178
Summary
180
Appendix A: Solutions
181
Appendix B: Glossary
189
Appendix C: Literature
199
Index
201
vi
About the Book This book is a rapid and effective Struts tutorial for Java developers. It is a step-by-step introduction to building Struts web applications. The book builds a fully-featured online bookstore application incrementally, with each stage described in detail.
What This Book Covers Each chapter begins with a short introduction and ends with in-depth questions and a short summary. Each chapter builds up on the previous one. Those readers who are already familiar with Struts can skip individual chapters, but they should at least skim through those chapters, so they won't lose the plot and it will also help them to keep up with the web application development part of the book. Here is an overview for the content of the each chapter: Chapter 1 presents an overview of Struts in compact form. Chapter 2 explains the installation for the Struts and shows the first, very easy example. The aim is to start working and experimenting with a fully running system. If you have worked with Struts before, you can skip this chapter. Chapter 3 introduces the concept the book is based on—the web application called the "Book Store". This will be built, expanded and adapted step-by-step in the following chapters. The underlying design will be introduced in this chapter. Chapter 4 discusses Internationalization—the possibility to adapt the text of a web site for different languages. Here are some further information and tricks: Custom Actions are explained and a particular Custom Action will be developed. Chapter 5 emphasizes the importance of correct logging and use of configuration data for software quality. This chapter presents different implementation approaches. Chapter 6 discusses forms. Handling forms is a standard problem. Error handling in particular generally requires the same mechanisms. Struts offers various possibilities for simplifying this task. Furthermore, this chapter gives insight on how to access databases. Chapter 7 introduces Logic Tags which allow us to manage conditional generation of a web page via HTML tags. Logical statements such as Loops and If statements are explained in this chapter.
About the Book
Chapter 8 discusses error handling, the central element of any application. A welldesigned shop will always display an error message; after all it's about the customer's convenience. Chapter 9 discusses the controller and templates. The basic process for the controller in the Model 2-concept is manually implemented and subsequently explained for Struts. Templates (Tiles) allow modular design of the web site and easy alteration. Chapter 10 is quite an extensive chapter where the elements of the shop are further expanded and merged together as one package. Authorization is implemented, a shopping cart is built and the order process completed. Chapter 11 offers interesting additional information on Struts independent of our online shop. Chapter 12 discusses JSTL—frequently called a successor of Struts. Here is a short overview of how these two techniques can be combined together. Chapter 13 gives useful tools and enhancements for Struts as well as practical solutions for frequently encountered problems. Appendix A is the Solutions section. Here you will find answers to several exercises. Do yourself a favor and look into this section only after you have solved the problem yourself! Appendix B contains a Glossary explaining important concepts. Appendix C contains Literature where you will find some more book tips.
Conventions In this book, you will find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning. New terms and important words are introduced in a bold-type font. Words that you see on the screen, in menus or dialog boxes for example, appear in our text like this: "clicking the Next button moves you to the next screen". Tips, suggestions, or important notes appear in a box like this.
2
About the Book
Reader Feedback Feedback from our readers is always welcome. Let us know what you think about this book, what you liked or may have disliked. Reader feedback is important for us to develop titles that you really get the most out of. To send us general feedback, simply drop an e-mail to
[email protected], making sure to mention the book title in the subject of your message. If there is a book that you need and would like to see us publish, please send us a note in the SUGGEST A TITLE form on www.packtpub.com or e-mail
[email protected]. If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide on www.packtpub.com/authors.
Customer Support Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.
Downloading the Example Code for the Book Visit http://www.packtpub.com/support, and select this book from the list of titles to download any example code or extra resources for this book. The code files available for download will then be displayed.
Errata Although we have taken every care to ensure the accuracy of our contents, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in text or code—we would be grateful if you would report this to us. By doing this you can save other readers from frustration, and help to improve subsequent versions of this book. If you find any errata, report them by visiting http://www.packtpub.com/support, selecting your book, clicking on the Submit Errata link, and entering the details of your errata. Once your errata have been verified, your submission will be accepted and the errata added to the list of existing errata. The existing errata can be viewed by selecting your title from http://www.packtpub.com/support.
Questions You can contact us at
[email protected] if you are having a problem with some aspect of the book, and we will do our best to address it
3
1 Introduction to Struts This chapter is intended as a fast-track introduction to Struts for those of you who are new to it. If you already have Struts experience, you may wish to skip this chapter and proceed directly to Chapter 2, where we get started on building the sample application. However, you may find this chapter a useful refresher and reference while you work.
1.1 The Apache Jakarta Project The most popular product produced under the aegis of the Apache Software Foundation (http://www.apache.org) is without doubt the Apache HTTP web server, which holds about 70% market share. The Tomcat web server, a part of the Foundation's Jakarta project that can be run either as a plug-in for Apache or standalone, is gaining similar popularity. Apache projects are grouped according to technologies they involve, and are themselves broken up into sub-projects. Jakarta really is the place to go for open-source Java projects, and it can be found at http://jakarta.apache.org. There are a couple of exceptions, notably Torque, a very useful object-oriented database mapping tool, that can be found under the Apache-DB project, although it is written in Java. If you are not already familiar with Apache Jakarta, then it is strongly recommend that you take some time to browse around the site to get a general picture of the products under development. You're sure to find something interesting, and you may well find some particularly useful tool. For serious users of Apache products, the official mailing lists play a vital role. The archive for the mailings list can be found at http://marc.theaimsgroup.com/. Subscribe to the lists now (follow the Mailing Lists link on the Jakarta home page) rather than waiting until you need to ask for help. That way, you can scan through the questions and responses first and get a feel for how it all works. One warning though: in a typical day, a hundred or more messages are exchanged. So it is advised that you to set your email program to move them to a special folder that you can check at your leisure.
Introduction to Struts
Note that there is an archive of old questions. Netiquette dictates you should search that first in order to avoid making duplicate requests for help. If there is one area where open source is wanting, it's probably the documentation, which often is poor, out-dated, or both. The mailing lists help make up for this, and once you are used to them, you will find they mean you always have someone to ask if you are stuck. Open source versus proprietary is a hotly contested battle, and we'll see further advantages and disadvantages of both put forward by both camps. http://www.opensource.org/ is a good source for entry into this subject. If you would like to find out more about the philosophy behind open-source software, we highly recommend The Cathedral and The Bazaar, by Eric S. Raymond (O’Reilly, 1999 ISBN 1-56592-724-9).
1.2 The Struts Project "Welcome to Struts! The goal of this project is to provide an open source framework for building Java web applications." http://jakarta.apache.org/struts/
The dictionary defines a "strut" as a structural support, and the definition extends well to Struts: it provides the basic framework upon which you can construct an application. Struts was brought to life by Craig R. McClanahan in May 2000, who donated it to Apache, where it became a project under Jakarta. It has since become an Apache project in its own right. Version 1.0 was released in the summer of 2001, with the objective of creating a framework that cleanly separated logic and presentation, to make web applications more manageable and maintainable. The benefits of Struts for creating well-designed web applications in Java led it to gain respect and acceptance quickly. The JSP Standard Tag Library (JSTL), which provides some of the same functionalities offered by Struts, can be seen as a direct reaction by Sun to the impact that Struts has had. As Struts is provided under the Apache open-source license, everyone has full access to the source code, without charge and without having to pay royalties when Struts is used in a commercial product. The developer has several choices when choosing the technology for a dynamic website (or web application). Languages such as ASP, PHP, and Java Server Pages allow sites to
6
Chapter 1
be quickly put together by providing additional tags and constructs that can be mixed in with HTML. In effect, the HTML tags (tables and so on) are usually used for formatting and presenting information to the user. Usually sites obtain information from the user through HTML forms, which must then be processed, and errors, if any, must be handled appropriately. Struts makes it easier and quicker to build and maintain powerful interactive websites by creating a framework that encapsulates the essentials of a web application's structure. This does not mean that the task is child's play. There is a tendency for people who have developed small sites—perhaps during the course of a college or university degree—to become overconfident and overestimate their own ability. Rushing headlong into the development task to build a real-world web application that needs to handle large amounts of data and a number of users is much less likely to work. This may sometimes result in further development becoming almost impossible. Badly structured web applications become difficult to update with new functionality or styling, and common problems like form processing, error handling, and internationalization reappear in many different places. A framework such as Struts, where all these problems are already solved for you, represents a potentially huge jump in productivity. Also, the control of application flow for e-businesses—typically product display, selection (adding products to the cart), collection of order information, transaction completion—is easier with the central controller that Struts offers than with a number of loose, unconnected pages. The main drawback of using a third-party framework like Struts is that you must spend extra time familiarizing yourself with it. This must be considered as a worthwhile investment, even though it is hard to measure the success of such investment, as is often the case in software development. For instance, Struts applications are very well structured and so it is essential that the basic principles of an application are set out early, and initial effort is put into the building of the base classes. So you may spend a week mastering Struts, in which time no effort is put into creating the web application, and this must be factored into your project schedule. Not every project leader or customer will recognize the usefulness of these activities.
1.3 The Struts Framework This section explains the basic principles and concepts of Struts. A glossary is provided at the end of the book where the technical terms used in the text are explained. Struts-based websites are built to be easily modifiable and maintainable, and the internationalization and flexibility of design is deeply rooted. A strict separation is enforced between processing logic and presentation logic (the Model and View in Struts parlance), which has a natural consequence of facilitating component reuse.
7
Introduction to Struts
All the main configuration of a Struts web application is performed by struts-config.xml. Here mappings can be defined between aliases and classes or between aliases and JSP pages. It is also here that the connections between Bean, Form, and Action classes are established. This centralization gives a better overview of the structure of an application, and also allows individual components of an application to be more easily modified. One big disadvantage is that any change requires a restart of the server; it is not possible to make changes at run time. For just about every bean, there's a Form class and an Action class. These determine how classes are filled, what values are valid, and what happens then. Struts can be a little pedantic with this structuring, but it does allow extensive support for user input, error handling, and database access. The problem here is that the separation of Model, View, and Controller becomes difficult to achieve. What belongs where? Database access in the Action class, for example, is easy and efficient, but these classes cannot be replaced with non-Struts code, as you will see later on. Another increasingly important consideration for commercial websites is Internationalization. In Struts, this is achieved via central language files. The application.properties file can be easily extended to support new languages. The server will attempt to use the user's preferred language; otherwise, the default language is adopted. The internationalization in Struts complies with the standard internationalization mechanisms of Java. The drawback is that errors in individual texts can cause run-time errors. Run-time errors are the first errors your clients will notice when your site goes live. Being able to centrally define the validation that the Validator must perform and using its ability to decide upon JavaScript or server-side testing greatly simplifies building and maintaining robust forms for capturing user input. The price is that these central XML files can quickly become large and complex. In addition, the use of JavaScript leads to higher bandwidth usage because all possible pre-packaged JavaScript functions are included in the response. If you have any experience of older style JSP programming, or even if you don't, you may be wondering why it's so important to keep Java code out of JSP pages as far as possible. These are the three most often cited reasons: • • •
It enables a part of the developing team not to know any Java at all. It assists in the separation of the processing and presentation logic layers. It encourages developers to make reusable code.
1.4 Model Classes Model classes appear frequently in this book. There are two different types of model classes. The first are JavaBeans, which represent the state of an object. These are simple 8
Chapter 1
Java classes that define a number of private variables that are assigned a value by calling their set method, or the value is read using the variable's get method. The second type of class relates to the behavior of the application, that is, Business here. For example, the object to complete the task of placing an order. Logic classes. The actions provided by the beans are executed Customer object might work together with the Shopping Cart
Struts works in conjunction with JavaBeans to process forms. A typical example might be a Customer Bean, which is used in tandem with a set of forms to enable users to register. Here are a few tips to make things easier for you: Always use Packages. The servlet specifications demand it, and it is why older Tomcat 3 applications cannot be executed in later versions of Tomcat. You will really need to understand the principles of Java packages. It is better not to start working with servlets directly, but work on the foundation a bit more.
Avoid naming your packages Model. While a common practice, this naming conveys nothing about what the package contains. Do all classes exist here? Or only beans? It's much better to use more descriptive names, especially for large-scale applications where the Model can be implemented by multiple Java packages.
Install a Development Tool. A good IDE, such as the Eclipse-based WebSphere Application Developer (WSAD), can help avoid many errors and increase your productivity tremendously. For example, the validity of your links can be checked automatically, Wizards can help in building classes, etc. Eclipse 3.1 provides an add on that enables the editing of JSPs and the management of Tomcat (similar to WSAD 5.x). A tutorial for this can be found at http://www.eclipse.org/webtools/testtutorials/M2/tutorial1/wtptutorial-I.html.
9
Introduction to Struts
1.5 Design Patterns "Design patterns capture solutions that have developed and evolved over time. Hence they aren't the designs people tend to generate initially. They reflect untold redesign and recoding as developers have struggled for greater reuse and flexibility in their software. Design patterns capture these solutions in a succinct and easily applied form." [The Gang of Four] We cannot go into detail on the subject of Design Patterns in this section, and we focus only the pattern that guided the design of Struts—the Model View Controller (MVC). Design Patterns devised by The Gang of Four (GOF) are solutions for commonly recurring design problems. They can be considered as recipes that may be applied to overcome complex problems. One of their objectives is to help create software that is easy to maintain. Furthermore, the nomenclature of Design Patterns makes discussions on software much simpler: "For this problem I will use the Singleton Design Pattern" is much less of a mouthful than "For this problem, I will write a class and ensure that this class has only one instance", which is what the Singleton Design Pattern entails. Imagine you wish to become an expert at making, let's say, tomato soup. If you ask ten different Master Chefs for their own recipe, you will get ten recipes, which are similar in some ways but different in others. It is true for creating software as well: there is no single design pattern that can make a master software architect out of a beginner. There are many different ways to implement the various popular patterns and you can find many different studies of their effectiveness on the Internet. This can lead to misunderstanding and inconsistencies when different developers on a team are familiar with different interpretations of the same pattern.
1.6 The Model View Controller (MVC) "MVC consists of three kinds of objects. The Model is the application object, the View is its screen presentation, and the Controller defines the way the user interface reacts to user input. Before MVC, user interface designs tended to lump these objects together. MVC decouples them to increase flexibility and reuse." [The Gang Of Four] The idea of separating an application into two layers—one concerned with presentation and the other concerned with processing logic—and orchestrating the two of them with a central controller is about twenty years old and time-tested. 10
Chapter 1
The upshot for the developer is that the View portion is constructed using Java Server Pages (JSP) and should contain little or no Java at all. For the Controller, a central servlet provided by Struts is used, and Taglibs and beans implement the Logic portion. To understand the principle, imagine an application with a web component (such as our shop) and a Swing-GUI for local users. Both the web and local portions have to accomplish very similar tasks but will have different appearances. Naturally, as the developer you want to do as little work as possible. With MVC, you would choose to put every activity that is used by both (for example, database access) in the Model portion. The View portion would need to be different, using JSP pages for the web-accessible component and normal Java classes for the local client. The point is that while View components will need to be implemented twice, Model components can be used by both the web and the local components. We will of course learn about the many possibilities of Struts as this book progresses, but for Swing application development, you would work with events and listeners. JSP, introduced by Sun Microsystems, provides a nice way to combine HTML and Java. However, as discussed before, this leads to maintenance problems as well as hindering enhancements, and so now we have what is known as the Model 2 paradigm. A big problem websites face is the fact that the protocol underpinning the Internet, HTTP, is stateless. This means that there is no built-in way for a web server to link different requests any user may make. It can simply react to each request independently. The classic-listener principle, as we know from Swing, does not work in this case.
Exercise 1 Read about the Observer Design Pattern and look into the observer and observable standard Java classes.
1.7 MVC versus Model 2 The Model 2 concept is usually brought up in relation to JSP and Struts. The concept was coined in the JSP specifications. Initially, only JSP was responsible for all the work (Model 1). But in time, developers realized that these JSP pages are hard to maintain and thus the Model 2 principle was conceived. In Model 2, the central controller servlet is responsible for the main control of the application. JavaBeans and Taglibs form the foundation, leaving JSP responsible for only the presentation layer. Later in the book, we put this approach to practice and apply the Struts solution shown in following figure:
11
Introduction to Struts
Figure 1.1: Model 2
Figure 1.1 shows how the user (the client) accesses the central controller servlet via the URL requested by the browser. After that, the appropriate JSP pages, which use any required business classes from the Model, are accessed. The Model in turn uses any other required resources such as the database. Note that access to the controller is implicit—the user does not notice anything different. To configure an Action servlet, you use the element within web.xml to define the servlet instance, which is then mapped by the element. One of the child elements of is , which for an Action servlet must specify the class org.apache.struts.action.ActionServlet. The following fragment shows how the element is used: action *.do
The and elements in struts-config.xml make sure the correct JSP pages or org.apache.struts.action classes are invoked. When the data on a form is submitted, it is validated and the form receives the required processing or else is displayed again with an error message. The Action classes can process the control flow further or invoke objects of other classes to process it. An Action can do various things—such as adding a product to a shopping cart object stored in session—before passing control to further mappings. These mappings might refer to a JSP page, for instance, to display the shopping cart. Because each user has their own unique session, the page will always show their own particular products. The Action 12
Chapter 1
cannot itself have any knowledge of business logic. It is only responsible for delegating control and performing error handling.
Exercise 2 1. Trouble yourself to write a small Java GUI to store details of items such as books in a database. Aim to create base classes that can be reused to reduce your overall workload. 2. Plan how you would go about writing the software for an online shop so that it could be easily maintained and expanded. Also imagine that you have the listing for such an online shop, but the developer is not reachable, and there is almost no documentation. How would you get acquainted with the code? What would you need to know in order to implement a new function, "Show all old orders for a customer"? 3. Give a thought to which should be built first, the Model or the View. Or would you build both side-by-side? Think about why this is an important question to answer before the development task gets under way.
1.8 Controller Component: ActionServlet The ActionServlet instance is not only the central control instance of your application but also responsible for the initialization and clean up of resources used. Initialization is performed based on the values specified in the configuration files. For each request made to the controller, the process() method gets called, with the request and response objects passed in as the two parameters: HttpServletRequest and HttpServletResponse. This method determines which module should process the request and then invokes that module's corresponding RequestProcessor.process() method, passing in the same request and response objects as parameters.
1.9 Design Guidelines for Action Classes Here are some guidelines for building Action classes. Remember that you will be in a multi-threaded environment: only one instance of your class will be created and this instance will spawn threads to handle each request. The same is true of servlets, and the following guidelines apply equally to them. •
Only use local variables: Do not use instance variables (apart from constants). Local variables are created on a stack that is assigned by your Java Virtual Machine for each request. When an action is handled by local methods, data must pass between them using parameters exclusively, and not through instance variables. 13
Introduction to Struts
•
•
•
Conserve resources: Even though creating objects in session can seem like a quick way to solve problems, it can lead to scalability problems. For example, if you are using JDBC to connect to a backend database, and you store a separate connection for every user in session, your website could easily grind to a standstill if you experience a lot of traffic. For this particular problem, connection pooling is a preferred solution. Don't ignore error handling: Nothing diminishes confidence in a commercial site quicker than an ugly stack trace appearing when a customer tries to perform a transaction. Will the transaction be completed? What will happen to their data? Struts offers a variety of options to catch errors so that a more informative and readable error message can be displayed. Avoid long and complex action classes: You should embed as little business logic as possible in your Action classes, to increase their maintainability, reusability, and readability. In fact, one of Struts' own MailReader example makes this same mistake of burying business logic in the Action class. The documentation confesses: "[it] should be considered something of a bug in the design of the example, rather than an intrinsic feature of the Struts architecture, or an approach to be emulated."
1.10 Reflection, Introspection, and DynaBeans The reflection mechanism allows Java code to process the bytecode resulting from the compilation process. It allows the developer to define and call the parameters from .class files, and is essential for many development tasks, such as creating an IDE. Introspection is a special form of reflection and is used by the JavaBean API. Here the methods must follow a particular naming convention to determine how those methods may be called. For instance, say your JavaBean has a property called Name. Then you would need the property accessors to be named getName() and setName() (and not, say, nameSet()). Struts uses introspection to fill a form according to the parameters in the request and to then call the JavaBeans. If you'd like to find out more about introspection, try the online Sun tutorial (http://java.sun.com/docs/books/tutorial/reflect/) on the subject. Form processing is a task that occurs very frequently. Writing simple beans for validating these forms can be a repetitive task. Struts aims to fix this with DynaBeans packaged with the Jakarta Commons JAR file (http://jakarta.apache.org/commons/beanutils/). With the help of an XML descriptor, they can be configured dynamically at run time. Instead of defining one JavaBean for every form—that needs to be recompiled and the server restarted on every modification—the relevant properties are declared only once: 14
Chapter 1
We'll be using this simple example later. The mechanism doesn't rely on the generation of JavaBeans, rather a map is created that has the required classes and their properties embedded in it. These can be extracted through introspection. If you do not initialize your objects implicitly, then numbers will start with the value zero, and other objects will be set to null.
1.11 Session Even though HTTP is stateless by nature, there are several techniques to identify individual users. The easiest is to include hidden fields within your HTML forms.
Such fields do not appear in the browser display, but will be sent with the rest of the form data that the user has entered when he or she submits the form. Hidden form fields are far from ideal however: they result in extra data traffic and the user can see them anyway by choosing View Source in their browser. They are one way that malicious users can attempt to mount an attack on your web application. It is also possible for an unauthorized third party to view the data they contain by intercepting a user's requests. This last problem can be eliminated with the use of SSL. Cookies are another simple way of identifying users. They are very short text files stored on the user's computer. While they are safe to use in updated browsers, their reputation is far from unblemished, and for that reason you can't rely on all your users having them switched on. Give it a Thought! For a particular project, an entire web interface based on cookies was created. Then a couple of e-mails were received from users complaining that the site didn't work for them. The entire site was converted, which took several weeks, reasoning that for each user who complained, hundreds probably never bothered, and simply never came back.
15
Introduction to Struts
The most elegant method is the use of a single session. Session is a Java object, much like Map, for storing other objects with a given name (or key). Each user is linked to their session either via a cookie or via URL-rewriting. You should always use the latter, because subsequent changes are cumbersome to perform. However, if your application will be deployed on a private intranet where the environment can be "dictated", using cookies might be acceptable. The servlet specification advises to store only serializable objects in session. You can, however, store other objects, but such objects will not be able to be restored in the event of a system failure. Servers such as WebSphere Application Server claim to be able to manage failures without losing such data, although you must be able to buffer the data. Another point to remember is that many servers can release session objects from storage as soon as they are buffered, allowing them to handle more users. Session length Avoid overusing session; remember that every user will have his or her session on the server. There are projects where the developer allowed the session to grow to 40 MB. Naturally, as more and more users tried to access pages simultaneously, the demand on server memory increased very fast and quickly became unmanageable. Normally a session should be no more than one or two KB, but the maximum size depends on how many visitors you expect. The following JSP code demonstrates how to store and retrieve items from session. It starts by storing a string value with setAttribute(), and a HashMap containing three further string values. The code then iterates through an enumeration displaying every session item in the browser, as well as writing it out to a file. Finally it displays the size of the session. Listing 1.1: SessionStorage.jsp
1.12 Business Logic In order to achieve maximum reusability, the business logic beans should be implemented as generally as possible, avoiding specifics of any particular web application. If you find yourself importing the javax.servlet.* classes, stop and rethink your design. You should try to get your Action classes to call the Property methods, rather than working directly with servlet methods. Note that depending on the complexity of the application, the business logic will be implemented either in regular JavaBeans or in Java Enterprise Beans. This does not make any difference for Struts.
1.13 Threads The thread safety of an application is one of those things that developers like to sweep under the carpet, and hope it'll be OK. However, this is a mistake and learning to ensure that your classes are thread safe will pay great dividends in the long run. Thread safety is something that won't necessarily present problems in the ad hoc tests a developer might perform on their code, when everything might function smoothly. Once two customers perform the same action simultaneously, thread safety immediately becomes an issue. For example, customer A orders some books that customer B ordered a split second before, and suddenly customer B finds their shopping cart empty. Such mistakes are hard to trace, because they are very hard to reproduce.
17
Introduction to Struts
Java offers good protection from concurrency, although it involves many details that can make one's life difficult. Unsynchronized access over the Web makes the problem worse, particularly during testing. The easy solution is to use tools such as JMeter, which can simulate heavy traffic. Virtual users access the site, placing load on the server and then the manual tests are executed. This increases the chances that such errors will be caught. Struts is itself thread safe as you might expect. It allows class instances to be reused and saves important resources. This relieves the burden of ensuring thread safety, but does not eliminate it altogether. You should still check for problems when your code accesses files or the database. Further details on web applications and threads can be found at http://java.sun.com/webservices/docs/1.0/tutorial/doc/Servlets5.html#64386.
Exercise 3 Search the Internet and find out about the Dining Philosophers problem. What is it, and what solutions are there to solve it?
1.14 Expandability Struts is expandable. You have the code and can easily reach the developer of that code over the mailing list. You are free to expand given classes and to increase their functionality. Of course, you will need to plan your steps thoroughly and format your problem exactly. Before posting questions, check the mailing lists to see if the solution is already available. Struts is used worldwide and so the problems and solutions are repeated frequently. Also be aware that Struts is continually changing from version to version, and your modifications may well become obsolete or at least require significant revision.
1.15 Debugging Struts Applications Struts offers no help in debugging applications. Modern IDEs, such as WSAD, offer a range of debugging features. They can set the breakpoints at the point where the execution is suspended and you can examine the contents of variables or step through the application line by line. Most of the time, though, log entries are used for debugging. These are discussed in detail in Chapter 5.
1.16 Some Experiences Here we discuss the pros and cons of Struts for real-life development. The following summarizes a few personal experiences and also includes the views expressed by a couple of developers with different fields of expertise. 18
Chapter 1
1.16.1 Struts in Universities and Colleges Some readers might be learning Struts as part of university or college tuition. If you are in such a situation, try to convince your instructor to set you a challenging application to develop in conjunction with several other developers working as a team. Or even better, one group writes the application and then the other group has to expand it and maintain it. Such an approach is perhaps the only way to get a grip on the techniques and the problems. A struts-config.xml with ten statements is easier to maintain than one with 100 or 1000 statements. Short mapping names help shrink the file length, but in a complex project, long names tend to be more useful.
1.16.2 Struts in the Enterprise If you ask developers about their experiences with Struts, you will hear one theme resurface many times: support for forms and multilingualism. Every now and then, you will come across a developer who believes that JSTL or Java Server Faces is a better alternative. Experience has shown that one needs to be wary of such arguments as they are often based on some article or tutorial and not practical project experience.
1.16.3 Struts from a Tester's Point of View In larger projects, there is usually a separate test team. The developers remain responsible for conducting their own tests on the code (usually with JUnit) and the testers test the user transactions. The testers look at the application as a black box and the best ones have a sense for errors that a developer would never think of. The principle of conducting unit tests on business logic is widely accepted and put into practice, but there is a view that user interface components cannot be tested at all or can be tested only at substantial expenditure. There are many possibilities for testing websites: • •
HTTPUnit (http://httpunit.sourceforge.net/) Cactus (http://jakarta.apache.org/cactus/)
As for Struts, two things to watch out for are: •
Forms are often filled in over several sequential steps (such as entry, confirmation, error correction). It's not enough to use HTML parameters to hold the data entered in the form fields, and session objects must be used. Tests for such forms can rapidly become very costly, and small changes in struts-config.xml can have enormous effects.
19
Introduction to Struts
•
Multi-language files tend to diverge quickly. Some entries may be missed out or just copied from the default language, or worse, incorrectly translated. This is very hard to test. Only errors where an entry exists can be traced by calling all pages. A small crawler, written with HTTPUnit, can check all links for validity and also find certain words in the accessed sites such as 'error', 'exception', and so on.
1.16.4 Struts in Project Responses of two developers from Switzerland who worked on an extensive project (following the Rational Unified Process model, with occasionally over 40 project participants) about the advantages and disadvantages of Struts are put together here.
Developer 1: Björn Advantages • •
• •
Struts requires little training time, thanks to the clear and logical separation into "pure" JSPs, Actions, Forms, and XML configuration files. The Tiles framework bundled with Struts (but disabled by default) offers a pleasant alternative to frames. A strong design allows good reusability of Actions, Tiles, and concurrent JSPs. 99.9% scriplet-free JSP pages. Good Taglibs (for example, logic).
Disadvantages • •
•
Names of attributes in struts-config.xml are often not intuitive. Debugging is especially difficult, because of unclear error messages, such as Form bean not found, and frequent failure to log exceptions. This is particularly bothersome with trivial errors like typing incorrectly cased letters in form names. The Validation Framework (validation.xml) is mostly unusable and also harder to execute than individual validation in the Form class.
Conclusion •
Struts will be here for a long time.
Developer 2: Thorsten Advantages •
20
Clarity—the flow of the pages (although there are exceptions).
Chapter 1
• • •
Internationalization and debugging. Code can be removed from JSPs due to the extensive Taglibs. As widely popular open-source software, there is good support through mailing lists and forums.
Disadvantages • •
Constantly changing naming convention, which is often inconsistent. Tiles and Validator do not have good tool support, although there is work going on in this area. For example, the Struts Console (http:// www.jamesholmes.com/struts/console/), briefly mentioned in Chapter 13, offers some helpful features.
Conclusion •
No application should be made without Struts!
Summary Struts is well suited for extensive (from the development point of view) Java-based websites. It is definitely not a finished diamond and it has many rough edges, which can cause confusion. Despite very good experience with its usage form validation and many other invaluable features, there are a couple of words of warning I'd like to give before we get stuck into the rest of the book. Internationalization with Struts is very straightforward, but that alone does not justify adopting Struts because it could be readily implemented by other means. The official documentation is woefully inadequate, but this is compensated for by the wide range of books and excellent mailing lists. The interchangeable design offered by Tiles can be seen as an unnecessary complication. Here are a couple of reasons: •
•
The basic intermix of the design is nothing but the movement of frames. Pictures, fonts, and colors make a site much more complicated, but not necessarily any more compelling. The success of sites like EBay and Amazon lies in the fact that these sites have a simple no-frills design and that the modifications are phased in gradually.
Whether or not you agree with these views on Tiles, by the end of this book, you'll hopefully agree on the overall effectiveness and value of Struts. In time, we will see to what extent Java Server Faces and the JSTL can live in harmony with Struts, or whether, as some predict, Struts will come to replace them.
21
2 Hello Struts This chapter takes you through the installation process of Tomcat and Struts. Once set up, we work through a simple example, so that you can start experimenting with an executable application as soon as possible. It is assumed you already have Java installed. If not, get the Enterprise Edition SDK from http://java.sun.com.
2.1 Tomcat Server Installation Tomcat 5.5.9 (http://jakarta.apache.org/tomcat/) is used for all the examples in this book. The examples should work with all versions of the server, but beginners are urged to use the same version or later to eliminate a potential source of errors. In any case, you should use at least version 4.x. Many readers would like to ask why Tomcat server was chosen from all the available servers. Some of the reasons for choosing Tomcat are that it is free, you can view the source code, and it is very widely used. Sun Microsystems recommends Tomcat as the reference implementation for servlets and JSPs. Furthermore, if we had shown how to run Struts applications on different kinds of servers, the book would have become too lengthy and much harder to understand. If you are installing Tomcat on Windows, you can download either the EXE or the ZIP file from Apache. The EXE is a Windows installer package, and takes care of registering the services and the like if you choose. You can choose the default location or another directory if you prefer during installation. Alternatively you may prefer the ZIP, which you simply extract to the desired location on the server. If you wish to run Tomcat as a service, simply execute the server.bat batch file in Tomcat's bin directory to register it. Once registered, it will be listed in the Services tool found within Administrative Tools.
Hello Struts
Figure 2.1: Tomcat: Standard Installation
Additionally, set up the following environment variables for Tomcat: •
JAVA_HOME: This must c:\j2sdk1.4.2).
•
CATALINA_HOME: This must example, c:\tomcat).
point to your Java home directory (for example, point to your Tomcat home directory (for
It's also worth adding Tomcat's bin directory to your system path. Set environment variables. You can set environment variables in Windows NT/2000/XP from the System Properties dialog (right-click My Computer and choose Properties). Go to the Advanced tab and click the Environment Variables button. In Windows 9x, you will need to add a line of the form SET to your autoexec.bat file for each one. If you have installed Tomcat as a service, you can start and stop it from the Services sub-application in Administrative Tools. You can also set it to Automatic to start Tomcat server whenever Windows starts.
24
Chapter 2
Alternatively you can start and stop it from the command prompt. At the command line, enter the command catalina start to start the server, and catalina stop to stop it. If you have used the Windows installer, you will find corresponding icons on the Start menu. A window opens and the server starts. Also note that if you have not added the Tomcat directory to your system path, you will have to give the path when you call the batch file, like this: %CATALINA_HOME%\bin\catalina.bat start absolute. Once Tomcat is running, you can check if it's working properly by visiting your browser, which should bring up the default start page.
http://127.0.0.1:8080/ in
Figure 2.2: Tomcat default startup page
The Documentation link is worth bookmarking for future reference. Also note that in place of the loopback IP 127.0.0.1, you can use the alias localhost. Run Tomcat on a different port. To run Tomcat on a port other than 8080, you have to change the value in c:\tomcat\conf\server.xml (search for 8080 and replace it with the new port number) and restart the server. As far as running Struts is concerned, no additional configuration of the server is required.
25
Hello Struts
2.2 Installation of Struts The latest production release of Struts at the time of writing this book was version 1.2.7. To install Struts for Windows, select the ZIP file from the Binaries link under Download on the Struts home page at http://struts.apache.org. The download contains a folder called webapps, in which you will find different WAR files (web application archives). Start Tomcat if it's not already running, and copy struts-blank.war, strutsand struts-examples.war to the webapps subfolder of your Tomcat installation directory.
documentation.war,
After a few moments, Tomcat starts unpacking the files and creating directories. Once your hard disk stops the clattering noise it makes when it's busy, you can try out the example by visiting http://127.0.0.1:8080/struts-examples/. When you have few moments to spare, examine the source code of the examples by looking in the folders that were created within webapps when Tomcat unpacked the WAR files. Building individual WAR files If you want to build your software from such packaged files, you can do it with standard tools provided with Java. From the command prompt, you would go to your webapps directory, and enter the following command: jar cvf myWAR.war myWebApp. With that, you generate a WAR file called myWAR.war for the web application named myWebApp. Also note that you can view the contents of a WAR file by opening it in a standard ZIP program. Various JAR files are required for the installation of the example from this book. JAR files are similar to WAR files, but they are executable while a WAR must be unpacked. As you proceed through the book, you'll need to follow the instructions to copy the respective JAR files either to a particular web application directory, or to the common/lib folder off your Tomcat installation directory. JAR files placed there are available to all web applications on the server. In general, the WAR files are very useful, but they are not without limitations for complex applications.
26
Chapter 2
Placement of the Struts JAR files Be aware that it's not advisable to attempt to make the Struts JARs available to every web application by placing them in the common or shared Tomcat subdirectories. This would be likely to cause problems, and recommended practice is to place the Struts JARs in the lib directory of each web application. See http://jakarta.apache.org/struts/userGuide/ configuration.html#config_add for the official word on this issue.
2.3 A Simple Page We are now done with the installation and will start with our first simple example. We'll create a basic welcome page, and display it in the user's preferred language. Here are the steps in brief: 1. Stop Tomcat. 2. Copy the directory struts-blank within webapps, and rename it as Shop1. 3. In the Shop1 folder, delete the subdirectories pages, WEB-INF/src and the contents of WEB-INF/classes (but leave the classes folder itself).
Figure 2.3: Directory Structure 27
Hello Struts
4. Create index.jsp in the Shop1 directory, deleting the existing file and replacing it with the code given in Listing 2.1. 5. Start Tomcat again. Listing 2.1: index.jsp Struts Tutorial: Shop1 Hello Struts
We can now open this page by going to http://127.0.0.1:8080/Shop1/ in our browser (Tomcat URLs are case sensitive). The simple page we've just created doesn't have anything to do with Struts, but it confirms that all is working as it should. If you cannot see the page, it's most likely that a configuration file was accidentally deleted (Tomcat will not show any error message in such a case). Stop Tomcat, delete your Shop1 folder, and repeat your steps one more time.
2.4 Internationalization with Struts Now that we have set up the web application (or context) and got it working, we can look at the internationalization features of Struts. Struts makes it very easy to present text in the user's chosen language. This feature is absolutely essential for multilingual websites, although it has some usefulness in handling certain other frequently used text, such as the standard copyright reference on the footer, etc. 1. Create a directory called resources in WEB-INF/classes. 2. Inside it, create two new text files called application.properties and application_de.properties. Here de denotes the German language. 3. Insert the lines index.title=Welcome in the first file, and index.title=Willkommen in the German file. 4. Modify index.jsp as shown in Listing 2.2. 5. Tell Struts where to look for the language files by changing the element in struts.config.xml (found inside the Shop1 WEB-INF folder) to the following:
Listing 2.2: Hello Struts (index.jsp)
28
Chapter 2
Every time a language file is altered, a reload is required. You can either restart Tomcat, or just the Shop1 web application. To do the latter, type the following URL in your browser: http://127.0.0.1:8080/manager/html/reload?path=/Shop1. Reloading and Restarting If you find yourself stuck, it's often a good idea to begin by restarting Tomcat. Certain errors, say in parsing configuration files, are only detectable this way, and reloading an application is not enough. When you request this page, you will see Welcome if your locale is set to English, or Willkommen if it's set to German:
Figure 2.4: Title in two languages
29
Hello Struts
Note that Internet Explorer allows you to change your preferred language by clicking the Languages button on the Internet Options dialog box. Other browsers, such as Netscape, also let you change the preferred language, allowing you to test pages in more than one language at once. You've probably guessed that Struts determines the language to use based on the user-agent information sent by the browser. If there are no suitable resource files, then it uses English as the default language (see the Exercises for a demonstration of this). Chapter 4 discusses Internationalization. If you change the preferred language setting in your browser, you will most likely have to close all browser windows and restart in order to put the change into effect.
Exercise 1 1. How would you go about displaying French text? 2. How would you use text that spans multiple lines? 3. Set your favorite language in your browser to some other value (for example; Spanish) and bring up the page again. 4. Skim through the documentation of Struts. 5. It was indicated in the chapter that English is the default language; is this correct? Swap the file names over: change application.properties to application_de.properties and vice versa. What happens now if you try to access the page using a preferred language that isn't supported?
Summary This chapter covered the step-by-step installation of Tomcat and the Struts Framework. We finished up with a basic JSP page that displays text in different languages using Struts' internationalization features.
30
3 The Struts Shop In this chapter, we start the task of developing an online bookshop that will help us to learn the fundamental concepts of building any such application. We also discuss the installation of the MySQL database and the creation of the first database table.
3.1 Why a Bookstore One problem when learning any new technology is that one gets introduced to only fragments of it. Unless how everything fits together to create a complete application is explained, it can be hard putting it all into context. By creating an extensive project during the course of the book, we will see how Struts is used in the real world. A bookstore is a very common choice of example for a web application. Its fundamental concepts are easy to understand and many parts are readily portable to other applications. Avoiding a long overworked list, the requirements are as follows: • • • • • •
Books and authors are stored by the system. Books can be displayed to users. Customers can register and later log on. Customers can add books to their shopping cart. A database is used to store the shopping cart (as well as other data). Transactions can be executed.
3.2 Iterations The shop will be developed in several steps or iterations to implement numerous functionalities as fast as possible, while still covering all the important ideas and concepts of Struts. For this, features will be developed twice: first in "classic" JSP without Struts, and then using Struts.
The Struts Shop
In some cases, the elements of the Struts solution will be the same as without Struts. In such a case, instead of giving the full explanation for the Struts part, the reader will be referred back to the relevant sections discussed earlier. It's quite common to find that appreciation of a technique comes only when you find yourself in trouble and experience the problems that the technique can avoid; and then you embrace it with open arms instead of resisting it. The development of the shop will follow several steps. First, the basic structure of the shop is developed and a multilingual welcome site is created. Then, we will create the Book and Author tables in the database. From there, we will delve further into Struts as we build up the shop's functionality in the coming chapters. Building the forms for customer registration and setting up session management will bring us considerably closer to our final goal. Lastly, we build the shopping cart and order functionality. The following list summarizes these steps: 1. 2. 3. 4.
Welcome site Enter the books and authors into the tables Customer registration Shopping cart and transaction system
Before we get started, a final cautionary note is in order. Just in case you were thinking of it: the finished shop will function perfectly well as it is, but it lacks the performance and security optimizations required for deployment as a commercial application. Additionally, today's online customer expects a greater diversity of features than the essentials that we cover here. With that said, let's move on and take an overview of the bookstore.
3.3 Elements of the Shop Figure 3.1 illustrates the shop's components. The individual Struts elements must work seamlessly together. For this reason, we will be using the Model 2 paradigm. The presentation of data for users is performed by JSPs. In the vast majority of cases, Struts means that Java elements can be completely omitted from these JSPs. The applications logic contains classes for the shop and storage for books, which will be written as regular Java classes. They could be used in total separation from Tomcat. So, for example, you could write a Swing GUI based on these same classes.
32
Chapter 3
Figure 3.1: Schema for the Struts Shop
Next we have a central servlet responsible for control. It evaluates the actions performed by users and calls the corresponding JSPs (see Figure 3.2). Struts protects this architecture, and we will get into more details about this in the later part of the book.
Figure 3.2: Model 2 Overview 33
The Struts Shop
Exercise 1 1. Why is Model 2 architecture used? Consider a finished application built using JSP and Struts. How much effort would be required to put such an application together? Remember that at the end it should be interesting to see how much your opinion changes as you gain experience of development with Struts. 2. Read up on the Internet about Model 2. Examine the opinions expressed in discussions on the mailing lists and try to form your own opinion. Repeat this exercise when you've finished the book and compare your thoughts.
3.4 Installing MySQL The storage of customer and book information can be handled by a single database. Although the main focus of this book is not on databases, a firm understanding of them is valuable to any web developer, and so an attempt is made to explain all the required steps carefully. Any previous database knowledge is not considered here, so experienced readers may wish to skip some or all of the following paragraphs related to databases. Note that more advanced concepts such as transactions are left out of this discussion. It is important to note that no real-world shop should be implemented without transaction support. If you would like more detailed information, search the online MySQL documentation. MySQL was chosen for the sample application, but the JDBC and SQL commands would be the same for other database. However, it is advisable that you use another database only if you are already familiar with it, because in this book we only look at MySQL. MySQL is free, very easy to work with, and very widely used, making help easy to find. Get the latest Generally Available (GA) release from the MySQL site at At the time of writing this book, the latest version was 4.1. In Windows, installation is straightforward if you choose the version with installer. As with Tomcat, you can run MySQL as a service, as explained on the following web page: http://dev.mysql.com/doc/mysql/en/windows-startservice.html.
http://dev.mysql.com/downloads/index.html.
A selection of tools is installed along with the database server, including winmysqladmin.exe, which lets you view and change the tables in a database in a userfriendly format. You'll find it in the bin directory of your MySQL installation folder. You need a driver for accessing the database using JDBC. Here we have used the MySQL Connector/J, which can be obtained free of charge by following the link on http://dev.mysql.com/doc/mysql/en/java-connector.html. The JDBC JAR file (mysql-connector-xxx-bin.jar) must be available in the classpath. The easiest way to do this is simply copy it into Tomcat's common\lib directory, where it will be available
for all web applications.
34
Chapter 3
In Windows, you have automatic access to the database on a local PC. Readers using other operating systems (for example, Linux in a University) should have the rights to create tables. You should choose a suitably secure password for the super-administrator database user known as root.
3.5 Tables In this section we look at the SQL commands for creating and dropping (that is, deleting) database tables. Open a command prompt, and start mysql, or click the shortcut in the MySQL folder in the Start Menu. If the MySQL bin directory is not on the system path, either add it, or include it when you enter the mysql command. This opens the MySQL console where you can enter SQL statements. You may prefer to use the MySQL administration GUI, but it is not necessary and is in fact not as powerful as the console.
Figure 3.3: Creating the database
The first command we're going to use creates a new database for our shop application, and then we set it as the active database with USE: mysql> CREATE database shop1; mysql> USE shop1;
Now we must create the necessary tables for authors and books: CREATE TABLE author ( id INTEGER NOT NULL AUTO_INCREMENT, Name VARCHAR(50) NOT NULL, Firstname VARCHAR(50) NOT NULL, CONSTRAINT id PRIMARY KEY(id) ); CREATE TABLE book ( ISBN INTEGER NOT NULL, Title VARCHAR(50) NOT NULL, 35
The Struts Shop
Author INTEGER NOT NULL, Price INTEGER NOT NULL, CONSTRAINT id PRIMARY KEY(ISBN), CONSTRAINT fk_author FOREIGN KEY(Author) REFERENCES author(id) );
Remember that case and whitespace are ignored by MySQL, and that almost all commands must end with a ; (semicolon). The CREATE statement will fail if the table already exists, so if you missed something the first time and want to repeat the command, first delete the old table using the DROP TABLE ; SQL statement. The tables we've just created have a primary key of author.id and book.ISBN respectively. A primary key is a unique column in a table, and represents a way to uniquely refer to any row (that is, any record) in the table. It can also be composed of more than one column, as we will see when we code the shopping cart. The book table uses the author primary key to uniquely refer to the book's author. This is called a Foreign Key, and we tell MySQL about it using the CONSTRAINT subclause when we create the book table. The relationship between author and book is that an author can write more than one book, but we will assume that a book always has only one author. This is a one-to-many (or 1:n) relationship, as opposed to a many-to-many (or m:n). To see the details of a table, use the DESCRIBE query as shown in Figure 3.4.
Figure 3.4: The MySQL DESCRIBE command
We will build the customer table later, as we don't need it now.
Exercise 2 How can we store the data in files instead of a database? What advantages and disadvantages would this have? 36
Chapter 3
Summary We have learned how to install MySQL and the JDBC driver and we've created a database with tables for books and authors. The foundation stone of our online shop has been laid, so let's move on to the next chapter.
37
4 Internationalization and Taglibs In this chapter, we create the fundamental structure for the shop, illustrating key concepts of the Struts framework. We also look at configuring our web application to support multiple languages. Finally, the Custom Actions, or Tag Libraries, are introduced.
4.1 The Structure for the Shop We constructed a simple Struts-capable website in Chapter 2. We can use that as a starting point here. Simply stop Tomcat, copy the shop1 folder in webapps, and rename it as shop2.
4.2 Internationalization Internationalization is a complex and involved subject. In brief, it refers to the automatic rendering of an application in the user's chosen language. It relates not only to the text itself, but also to numbers, date format, and currency values. Special symbols and alphabetical sorting in different languages bring interesting and unexpected problems with them.
4.2.1 Internationalization and Java Java offers a variety of internationalization options for applications. The following two tutorials on Sun's website cover the subject quite well: •
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/ WebI18N.html#wp76431
•
http://java.sun.com/docs/books/tutorial/i18n/resbundle/ concept.html
Internationalization and Taglibs
Let's take a quick look at a very simple example: Listing 4.1: Test.java import java.util.Locale; import java.text.NumberFormat; public class Test { public static void main(String args[]) { String amountOut; NumberFormat numberFormatter; Double amount = new Double(345987.246); Locale en = new Locale("en", "US"); Locale de = new Locale("de", "DE"); // US output numberFormatter = NumberFormat.getNumberInstance(en); amountOut = numberFormatter.format(amount); System.out.println(amountOut + " " + NumberFormat.getCurrencyInstance(en).format(amount) + " " + en.toString()); // German output numberFormatter = NumberFormat.getNumberInstance(de); amountOut = numberFormatter.format(amount); System.out.println(amountOut + " " + NumberFormat.getCurrencyInstance(de).format(amount) + " " + de.toString()); } }
This will give you the following output: 345,987.246 $345,987.25 en_US 345.987,246 345.987,25 Ç de_DE
The German currency should, of course, use the Euro symbol; a wrong character appears here as a consequence of the limited character set available in the DOS console. Java allows us to define the text for several languages with the help of configuration files. A suitable language is chosen depending on the user's system language. A similar mechanism applies with Struts as well, but instead of the system language, it uses the language specified by the browser (see section 4.4, Custom Actions).
4.2.2 Internationalization and Struts One of the limitations in Struts is that it does not offer any facility to accept input from languages that have special symbols. This is a particular issue for Asian languages where the browser has to provide support. 40
Chapter 4
In Chapter 1 you saw how a page can be multilingual. How does Struts know where to look for the correct text for the language? The WEB-INF/struts-config.xml file contains the following entry:
The first part of the parameter attribute value, resources, indicates the name of the subfolder within WEB-INF/classes that holds the language files. The second part, application, gives the filename "stem" of the language files (without the .properties extension and country code). A full list of country codes is at http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt. So if you wish to store your language files somewhere else, you use this method to specify their location. Don't forget that you must reload the application context again whenever you adjust any configuration or language files. You can organize your language keys within the language files in the following form: classname.definition [.]
You can group the keys according to how frequently you expect to use them. Have one row of single words, such as general.street=street. Always make sure to define all the required keys, as it enables error messages to be generated correctly. The language files used above show the problem with the naming convention. It is often not clear how to name each part and one tends to choose the first thing that comes to mind. In this case you may end up with many placed under Author, for instance, even though they would be better placed in the general category. Such little details can be really beneficial as you use the files in a day-to-day context. It is much simpler to correct such mistakes immediately, rather than trying to remember illogical key conventions. Managing the properties files Many development environments can help manage the properties files. The Netbeans IDE, which comes with the Java SDK, shows keys sorted alphabetically and you can work with more than one language file at once. In long files, the alphabetical ordering turns out to be difficult to work with, and so you may prefer to use your own sorting and a simple text editor. Experiment with this a little bit! One time-tested approach has been to separate the language files into smaller files and then consolidate them with Ant. This allows different developers to maintain individual files. The problem is to clearly define the categories for each file and stick to them since this approach can easily lead to omissions and repetitions. 41
Internationalization and Taglibs
4.3 The Welcome Page for the Shop In Chapter 1, we wrote a very simple page to demonstrate the use of language files. It is reproduced below: Listing 4.2: index.jsp
•
Line 1: This is a JSP directive (indicated by the enclosing ) specifying the language used in any scriptlets on the page. As Java is the default language, this line is often omitted, but is necessary if you are using a mixture of languages (for example, Java and JavaScript using JRun instead of Tomcat). • Lines 2 and 3: These are directives that act somewhat like import statements in normal Java classes. We need them if we wish to use custom actions on the page (see the Custom Actions section). • Line 5: The locale="true" in this line indicates that multilingualism is to be applied (see the exercises at the end of the chapter). • Line 9: This uses a bean tag (see the Custom Actions section), which represents an object of the org.apache.struts.taglib.bean.MessageTag class. The message bean is called with a key attribute of index.title. Struts now searches for suitable language files for the language specified by the browser and inserts the corresponding text in place of the tag. Visitors to our shop will be greeted with a welcome page in either their preferred language or the default language. Change index.jsp so that a company logo and a copyright notice are displayed:
42
Chapter 4
Listing 4.3: index.jsp
You'll need to create a new pics folder for the logo directly under the main shop2 folder. Choose a suitable image file to use and place it in the folder with the name logo.jpg. Relevant text for the general.copyright key must also be added to the language files. Multilingual Images The Struts tag allows different languages to use different images, each with different alt text:
And then the following lines are required in the language files: button.clickMe.src=/images/button_clickMe.jpg button.clickMe.alt=Click Me
See http://jakarta.apache.org/struts/userGuide/ struts-html.html#img for more details.
Exercise 1 Create a second logo for another language. Change index.jsp to display the logo in the user's language. Be brave: you can mix normal HTML and Struts!
43
Internationalization and Taglibs
4.4 Custom Actions JSP supports user-defined tags or Custom Actions according to the tag extension mechanism defined by the JSP specification. Custom actions have access to all the information from the Request and Response objects, and can use variables in the session. A set of custom actions is called a Tag Library or Taglib. As Struts is simply a set of custom actions, it makes sense to know how they are created. Before going into details we'll run through a simple example. We'll create a small action that displays the date according to the user's local convention (refer to Chapter 12 for more on this topic). Custom actions are usually developed and deployed in JAR files. In our case though, we will be using the class files compiled from our source code as we write it, so create a subdirectory called mytags under WEB-INF/classes to hold the tag files. Create a new Java file in this directory and name it CountryDate.java. Enter the following code: Listing 4.4: CountryDate.java package mytags; import javax.servlet.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.text.DateFormat; import java.util.Date; import java.util.Locale; public class CountryDate extends javax.servlet.jsp.tagext.TagSupport { protected PageContext pageContext; public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } public int doStartTag() throws JspException { Date today = new Date(); ServletRequest request = (ServletRequest)pageContext.getRequest(); Locale user = request.getLocale(); DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, user); String now = dateFormatter.format(today); try { pageContext.getOut().write(now); } catch(java.io.IOException ex) { throw new JspException(ex.getMessage()); } 44
Chapter 4
return EVAL_BODY_INCLUDE; } }
The CountryDate class extends the interface javax.servlet.jsp.tagext.TagSupport. This ensures that Tomcat will automatically recognize it and call setPageContext() passing in environment information (request, session, etc.). The doStartTag() method is also defined by the interface. It is called whenever Tomcat encounters the action embedded in a JSP. In this code, this method gets the user's locale from the browser request, and uses it to create a DateFormat object based on the default date format for that locale. Finally, we get today's date—suitably formulated using the format() method of the DateFormat object—and write it to the current output stream. This is usually the response page, which the user sees. If you try to compile the class now, you will get an error message saying: C:\Tomcat\webapps\Shop2\WEB-INF\classes>javac mytags/*.java mytags/CountryDate.java:3: package javax.servlet.jsp does not exist import javax.servlet.jsp.*; ^
We see this because the JSP and servlet packages, servlet-api.jar and jsp-api.jar, are not part of the standard Java packages and so must be added to the classpath. Adding the JSP and servlet packages to the classpath You can either add servlet-api.jar and jsp-api.jar (found within Tomcat's common\lib directory) to your classpath environment variable, or you can specify them with the -classpath switch every time you compile with javac. A good idea is to make a batch file (a text file with a .bat extension) in your directory containing the following two lines:
classes
javac -classpath "%CATALINA_HOME%\common\lib\jsp-api.jar"; "%CATALINA_HOME%\common\lib\servlet-api.jar" mytags/*.java pause
As a batch file, you can simply double click it from Windows explorer to compile all the Java files in the mytags subdirectory. Note that if you added jspapi.jar and servlet-api.jar to your classpath, you can omit the classpath switch shown above. In order to use this custom action in a JSP, it must be declared and reported to Tomcat. The web.xml file, which we got when we copied the struts-blank template, contains various elements. These reference the TLD configuration files that define custom actions. Insert the following lines after the other Taglib entries in the web.xml for shop2: 45
Internationalization and Taglibs
http://www.stephanwiesner.de/struts /WEB-INF/taglib.tld
The element is a way to identify a particular Taglib uniquely, allowing two different Taglibs to contain a custom action with the same name but still be used at the same time. It doesn't have to be a valid URL; a company (or individual) URL is often used because the company can be fairly sure no one else will use that to identify their own Taglib. So naturally, you can replace my name with your name, but remember that the name you choose should be consistent in every place where it's used. The element specifies the name and path of the Taglib configuration file, here /WEB-INF/taglib.tld. Not surprisingly, you'll need to create this file as well. It's an XML file and should have the following content: Listing 4.5: taglib.tld 1.0 1.1 shop http://www.stephanwiesner.de/struts Taglibs for the Struts book countryDate mytags.CountryDate Displays a formulated Date
Make sure that the mytags.CountryDate package specification in the element is correct. The element contains some descriptive text. The element defines the name that JSPs must use when they invoke our custom actions. Now, restart the application context (or Tomcat) and we can use our new custom action in a JSP: Listing 4.6: index.jsp
46
Chapter 4
Notice the third taglib page directive (enclosed in ) that imports our custom actions:
The namespace given in the uri attribute must match exactly with the and elements in the configuration files. The prefix attribute specifies the prefix that will be used in the JSP page code to identify custom actions associated with this URL. So, to use the countryDate custom action, we use the following tag:
All this fuss with naming might seem unnecessarily verbose and tiresome. However, imagine another Taglib written by someone else, which also defines a custom action named countryDate, but which has a quite different purpose to ours. Because they are contained in packages that have different URLs, we could use both on the same page without any ambiguity: . . . . . . is not the same as
As you see, custom actions bear many similarities to normal HTML tags and the idea is that they can be used by web designers who know only HTML.
47
Internationalization and Taglibs
Figure 4.1: The countryDate custom action produces a different date format for different locales.
Like JavaBeans, custom actions make for simpler usage of frequently repeated functionality, without the need for Java code in scriplets to be embedded in the HTML. For further information, check out Hans Bergsten's JavaServer Pages, from O'Reilly (2000, ISBN 1-56592-746-X) or http://www.javaworld.com/javaworld/jw-082000/jw-0811-jsptags.html.
4.5 struts-bean.tld Now that you have a basic knowledge of Taglibs, we are ready to move deeper into Struts. Open up the file WEB-INF/struts-bean.tld in your preferred editor and search for message. You will find an entry, on or around line 149, which defines the action that you have just used:
The bean:message tag can take up to five parameterized arguments, numbered 0 to 4. We can assign a key and a scope to it as well. It is instructive to have a quick skim through the documentation and see how each is used. You'll find it at the following website: http://127.0.0.1:8080/struts-documentation/userGuide/struts-bean.html.
48
Chapter 4
TagUtils Struts comes with the very useful org.apache.struts.taglib.Tag-Utils class. This class contains a range of helper functions that you will find handy when building taglibs.
Exercise 2 1. What happens when you leave out locale=true in a JSP? What if you set locale=false? 2. What happens if a key of a message bean has no corresponding entry in the language file? 3. Enhance the welcome page. Change the logo and copyright info and write a short multilingual introduction. 4. How would you implement one more language (for example, Spanish)? How complex would it be? 5. Could you allow the user to choose the language explicitly, and if so how? For example, could you offer a button on the page? 6. Think about how you could provide more extensive text in multiple languages, such as a help page? The standard language files do not provide much help here, because they can only handle short texts efficiently. (Hint: Take another look at the first custom action example.) 7. Why shouldn't you work on the file WEB-INF/struts-bean.tld? Think about what would happen if you did add your own settings to that, and then a new version of Struts was released.
Summary This chapter provided a short introduction to Taglibs and custom actions, and took a first glance at the inner life of Struts. Our shop now has a welcome page, although it's pretty sparse and not so attractive yet!
49
5 Logging and Configuration The use of standardized logging mechanisms is an important part of any well-written software, and should be factored in from the very first stage of the development process.
5.1 Configuration It is useful to bunch configuration values together in a single file (or at least in as few files as possible). This not only simplifies maintenance, but it also increases security because you know exactly which files to back up. In UNIX, you can provide higher security by assigning authorization rights for configuration files. There is always a question as to which is the most elegant way to access the data. Naturally, you can read the files each time you need a value, but this does not lead to high performance, plus you will end up with a lot of redundant code this way. A Singleton Design Pattern (see Glossary) works well in most cases. Create a new directory called WEB-INF/classes/tools. Create a class named PropertiesSingleton.java here and enter the code shown in Listing 5.1. This class originates from one of the open-source projects (http://sourceforge.net/projects/ swiesnerxmlgui) and has been used in a lot of projects since. The constructor is declared private so that instances cannot be created directly. We obtain full control over instantiation of the class, and can ensure no more than one instance ever exists. The getInstance() method returns a reference to this instance. Listing 5.1: PropertiesSingleton.java package tools; import java.io.*; import java.util.*; public class PropertiesSingleton { private static PropertiesSingleton config; private Properties props; private static String configFile = "/resources/config.properties";
Logging and Configuration
private PropertiesSingleton(String configFile) throws IOException { this.configFile = configFile; props = new Properties(); String myPath = this.getClass().getResource( configFile ).toString(); props.load(this.getClass().getResourceAsStream( configFile )); } private PropertiesSingleton(Class baseClass, String configFile) throws IOException { this.configFile = configFile; java.io.InputStream in = baseClass.getResourceAsStream(configFile); props = new Properties(); props.load(in); System.out.println("Number:" + props.size()); } public String getConfigFile() { return this.configFile; } public static PropertiesSingleton getInstance(String configFile) throws IOException { if (config == null) { config = new PropertiesSingleton(configFile); } config.configFile = configFile; return config; } public static PropertiesSingleton getInstance() { if (config == null) { try { config = getInstance(configFile); } catch(IOException ex) { System.err.println("Error:" + ex); } } return config; } public String getProperty(String key)
52
Chapter 5
{ return props.getProperty(key); } }
This class creates a single instance of java.util.Properties, based on a properties file, which is read when the object is used for the first time. If no file is specified, a default file is loaded instead. Subsequent attempts to create an instance return the previously created instance, thus enforcing the singleton requirement.
Exercise 1 1. How would you rewrite the singleton class to create two, three, or ten instances? 2. It would be nice if configuration files were read automatically at every startup. How can you achieve this? 3. PropertiesSingletion.java does not contain any methods declared with the synchronized keyword. Should it be like this? For which sorts of methods is this keyword critical? In any web application, there is always a question of the file path. The configuration file should be read only once. The easiest way is to embed the path of the configuration file in the class. When a new directory is used, it will be recompiled. "What is the actual path?" is the next question. With Tomcat, the file path starts with the directory from which the configuration file gets started. Keep in mind that the classes should work not just with Tomcat, but also with other applications. Let's build a WEB-INF/classes/resources/config.properties file that will contain individual settings: log.level = 5
Insert the following values that we will require later on in the config.properties file (reload the context after these changes are made): # Log-Inputs log.level=5 log.size=150000 log.file=c:/shop.txt
To output these values, change index.jsp. You first need an import directive at the beginning:
Then you can output the values like this: 53
Logging and Configuration
5.2 Simple Logging This section looks at how messages can be logged without any special-purpose logging code, and what sort of limitations this approach brings. Basically, there are two options for the simple display of log messages. The simplest is definitely the following:
This would be coded directly in a JSP, and the output then shows up on the Tomcat console and in your log file respectively, without being visible to the user. Unfortunately, it has several disadvantages. The main disadvantage is that usually the production server is not located on your desktop. So there is no clear way to know if there is a secure console and who would see it. If the Tomcat log files are accessible at all, they might be accessible only over SSH or FTP. There are further problems, which are true even for the next approach. For example, log messages cost performance. In a production environment, we would prefer to turn them off, but still be able to turn them on when we need them again. Grouping log messages on priority saves resources in normal mode and helps when everything must be switched on for debugging. The other simple possibility is to directly embed debug messages in your JSP pages, so that they print the messages in the browser, and can be turned on and off on demand:
The reason why developers so often use System.out.println() is the sheer convenience. It is much faster to use this type of logging than writing some kind of special logging code (even if it is only a simple code). When the pressures of the real world are looming large, we tend to think that we don't have time for a better solution, and after all it's only something we use during debugging. However, it really is worth investing time and effort in constructing a more sophisticated logging solution. It's very useful, of course, in development, but even more so during maintenance, when we might need to find an error in the code after half a year or so, when System.out.println() just can't cut it.
5.3 Logging Solutions This section introduces the logging mechanism recommended by Struts, Jakarta Commons Logging. (A different approach is taken for our sample application as you will see in the following chapters.) The implementation is very simple. Here is a snippet from one of the Struts sample applications:
54
Chapter 5
Log log = LogFactory.getLog("org.apache.struts.webapp.Example"); if (log.isDebugEnabled()) { log.debug("SaveSubscriptionAction: Processing " + action + " action"); }
Now you can configure the logger the way you want it. The disadvantage is that the number of if statements bloats the code.
Exercise 2 Browse through the source code of the Struts example applications and look out for the use of logging.
5.4 JDK Logging With JDK 1.4, Java finally got its own logging mechanism, which allows us to view log messages over the web. In this section, we put this mechanism into practice. We will extend the standard logger classes to provide HTML formatting and build a JSP to display them. Sun has made it very simple to build custom log classes with special formatting, and HTML is definitely not the only way we could do it. Create and compile LoggerHtmlFormatter.java in the tools directory, as shown in Listing 5.2. Listing 5.2: LoggerHtmlFormatter.java package tools; import java.io.*; import java.util.logging.*; import java.util.*; import java.text.*; public class LoggerHtmlFormatter extends Formatter { public String format(LogRecord rec) { try { StringBuffer buf = new StringBuffer(1024); buf.append(getTime(rec.getMillis())); buf.append(" " + rec.getSourceClassName() + "." + rec.getSourceMethodName() + ": "); buf.append("" + formatMessage(rec) + ""); buf.append("(" + rec.getLevel() + ")"); buf.append('\n'); return buf.toString(); } catch(Exception ex)
55
Logging and Configuration
{ System.err.println("Error:" + ex); return ""; } } public String getTime(long time) { String format = "yyyy-MM-dd HH:mm:ssSS"; Locale currentLocale = new Locale("DEU", "DEU"); java.util.Date today = new java.util.Date(time); DateFormat dateFormatter = DateFormat.getDateInstance( DateFormat.DEFAULT, currentLocale); SimpleDateFormat formatter; formatter = new SimpleDateFormat(format, currentLocale); return formatter.format(today); } public String getHead(Handler handler) { return "Logging: " + getTime(System.currentTimeMillis()) + "\n"; } public String getTail(Handler handler) { return "\n"; } }
This class extends the standard Formatter class to format messages appropriately. The format() method of the DateFormat object is especially useful. Just about every way of displaying dates is made possible by this method. The getHead() and getTail() methods are called only once, and determine how the beginning and end of a log file should look. The importance of log files is clear when we have an error in our application, or worse, after it has completely crashed. For such reasons, it's important to write messages to the disk rather than buffering them for performance. If our server does crash, our HTML log files may well be incomplete—the footer could be missing. As they are HTML, this isn't a great problem and the files can still be displayed, although tables can get formatting anomalies. If we were creating XML log files, on the other hand, incomplete files would not be well formed, making them difficult or impossible to process automatically.
Exercise 3 Investigate the Formatter class by referring to the JDK API.
56
Chapter 5
5.5 Reading Configuration Settings It is now possible to initialize the logger directly, but the configuration parameters must be set. This has to be done only once, when the logger is used for the first time. Hopefully this reminds you of something; it's what the Singleton Design Pattern sets out to achieve. The LoggerInitiator class given in Listing 5.3 is initialized on the first call, irrespective of who called it. All subsequent callers then receive this instance. Listing 5.3: LoggerInitiator.java package tools; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; public class LoggerInitiator { private static Logger logger; private static PropertiesSingleton config; private LoggerInitiator() { } private static void init() { config = PropertiesSingleton.getInstance(); logger = Logger.getLogger("StrutsShop"); logger.setLevel(Level.parse(config.getProperty("log.level"))); try { FileHandler handler = new FileHandler(config.getProperty("log.file"), Integer.parseInt(config.getProperty("log.size")), 1); handler.setFormatter(new LoggerHtmlFormatter()); logger.addHandler(handler); } catch(Exception ex) { System.err.println("Error:" + ex); logger = null; } } public static Logger getLogger() { if (logger == null) { init(); } return logger; } }
57
Logging and Configuration
The LoggerInitiator class uses the PropertiesSingleton class shown earlier to read the configuration file and the LoggerHTMLFormatter class for the HTML formatting. We can get a logger by using the getLogger() method of the Java Logger class by specifying the Logger class name as the String argument. This approach permits different objects to use the same logger independently of LoggerInitiator.java. They must, however, exist within the same Java Virtual Machine.
Writing messages with our logger in a JSP is now very simple. For instance, add the following code to index.jsp:
This will create the following log entry: 2005-07-10 08:33:51852 org.apache.jsp.index_jsp._jspService: index.jsp functioning(INFO)
As you can see, the time, the implemented class, and the message appear together. Now if the log file is placed somewhere accessible, such as within a password-protected folder that can be accessed from the net, it can be viewed over the net. Generally, a JSP (like the one shown in Listing 5.4) that can read and display the log file, is created. This method is easy to maintain and different programmers can view the file simultaneously while keeping it hidden from unauthorized eyes. Listing 5.4: viewlog.jsp Logging
The JSP reads the configuration file to determine the location of the log file. Then the log file is read via a byte array and sent to the browser. Figure 5.1 shows the typical output:
58
Chapter 5
Figure 5.1: JDK Logging
5.6 Logging with Log4J Log4J (http://logging.apache.org/log4j) is a popular logging library that is simple in use, very flexible, and effective, although at the price of extra complexity. A detailed discussion of Log4J is out of scope of this book, but a good tutorial can be found at http://www.vipan.com/htdocs/log4jhelp.html. The configuration file should be placed in WEB-INF/classes, called log4j.properties by default. And of course the Log4J JAR should be in the classpath, for example, in the lib directory of your web application. A typical Log4J configuration file looks something like this: Listing 5.5: log4j.properties log4j.rootCategory=INFO, stdout, fileout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) -%m%n log4j.appender.fileout=org.apache.log4j.FileAppender log4j.appender.fileout.layout=org.apache.log4j.HTMLLayout log4j.appender.fileout.append=false log4j.appender.fileout.File=c:/shopLog.html log4j.appender.fileout.layout.Title=Struts Shop Log. log4j.appender.fileout.layout.LocationInfo=true
According to this configuration, log messages are sent both to standard output (the console) and to the file c:\shopLog.html. The file will be written in HTML format, and the class as well as the error message will be recorded.
59
Logging and Configuration
To use the logger, place code like this in a JSP:
Figure 5.2: Output from Log4J
The output shows the error clearly. Interestingly, much more than what we asked for gets logged! This is because Struts itself uses the Jakarta Commons Logger. Struts doesn't configure logging itself, it's all done by the Commons Logger under the covers. The default choice is made according to the following rules: • • •
If Log4J is there, use it. If JDK 1.4 is there, use it. Otherwise, use SimpleLog.
The advantage here is that only the debugging references are given. But it could also mean that we don't get the details we need and have little control over information recorded. It is up to you to decide which method you find best. The Struts documentation on logging can be found at struts-documentation/userGuide/ building_controller.html#logging.
Exercise 4 1. Why does declaring the error statement help us much less in JSP than it would in a normal class? 2. Set the file path for Log4J to c:/ instead of c:\. Reload the context. What happens to the log file? Where do you see the error message (if at all)?
60
Chapter 5
5.7 Jakarta Commons Logging There is a great need for debugging and logging information inside Commons components such as HTTPClient and DBCP. However, there are many logging APIs out there and it is difficult to choose among them. The Commons Logging package is an ultra-thin bridge between different logging libraries. Commons components may use the Logging API to remove compile-time and run-time dependencies on any particular logging package, and contributors may write log implementations for the library of their choice. For more information, visit http://jakarta.apache.org/commons/logging. We saw in the previous section that Struts uses Commons logging components. The package offers flexibility of choice between different logging mechanisms. It allows, for example, painless switching between Log4J and JDK 1.4. Using code can be very cumbersome and so should be avoided completely.
5.8 Deciding What to Log This decision of what to log is not an easy one. Write log entries when you search for an error. Use different log levels to filter out the messages. The Pareto principle says that 80% of the errors are found in 20% of the code. This saying has not been verified, but at times there are log entries for the same errors again and so there must be some truth in it. Assign a 'level' to log messages Remember to assign a meaningful level to your log messages. In the shop application, the "INFO" level is used, which means we can see what is happening just from a glance at the Tomcat console. Often it helps to log a message at the beginning of a method. This message should list all parameters that were passed. This is a great trick for spotting null pointers.
Exercise 5 1. The LoggerHTMLFormatter class explicitly uses a German date format. How can you change the class to use the server's locale? 2. Why does the LoggerInitiator class have an empty constructor? Can an empty constructor be left out? 3. The class introduced in this chapter uses JDK logging. Refer the Java API to find all the available log levels, and play around with the code a little bit. Set 61
Logging and Configuration
the log levels in the configuration file to different values and see what entries are displayed.
Summary In this chapter, you have been introduced to the use of configuration files and logging for debugging. Various techniques for handling configuration settings and for implementing logging are described further on in this book. In the end, of course, it'll be up to you to decide which techniques are most useful to you.
62
6 Forms One of the strengths of Struts lies in the processing of form input fields. This chapter deals with forms and is the most extensive and definitely most challenging chapter of the book. We will first create a new context called shop3 by copying the shop2 folder. We will start with a simple example and then move on to a more complex one.
6.1 Preparation Although form processing in Struts is very helpful, it is also very intricate. Rather than giving a long and potentially confusing explanation, we will take a look at how form processing works in smaller steps. We will start entering data into the Book table. We will introduce a new technique in the first example. You may not understand everything that is going on until you have built the complete example and tested it. Everything will become clear as you proceed. Please keep in mind that our work will get much easier when we extend the Validator, as explained in Chapter 11. The first thing we want is a bean that represents a book. Create a new directory for this purpose called WEB-INF/classes/book, and enter the code below as Book.java. Listing 6.1: Book.java (first version) package book; public class Book { private String title = ""; private String author = ""; public Book() { } public void setTitle(String title)
Forms
{ this.title = title; } public String getTitle() { return this.title; } }
At the moment, Book.java is pretty much a simple Java bean. Now we will build a small form where the user can enter book names and error messages will be shown if the wrong information is entered. Naturally, we will internationalize it. Copy index.jsp and name it Book.jsp. Remove the tags relating to the welcome messages, make sure the Struts Taglib directives are there, and add the following lines: Title:
These statements use the Struts HTML tags to define the HTML form with an input field for the title and a submit button. We still need to add the functionality of the form that this one submits to, the createBook alias given in the action attribute of . The Struts tag handles display of any validation error messages detected when the form is submitted, which will appear in a row. We look at formatting these messages more usefully in Section 6.2.2, Formatting the Error Message Display. The question of whether we should have the input and output confirmation forms on a single JSP that submits to itself has been discussed on the Struts mailing list. (You can check the 01.10.2003, Same JSP for view and edit mailing list.) The accepted conclusion is that it should be avoided, so copy Book.jsp and rename it BookView.jsp. Write View Book Details in the file, to help differentiate between the two—except for this, the two JSPs are identical. Compile Book.java from the command line using the following command: mysql> javac book/*.java
If you try to open the JSP now, you should see the error message shown in Figure 6.1. This occurs because we haven't yet set up the mappings that are used on the form:
64
Chapter 6
Figure 6.1: Mapping Error
The mappings must be placed in WEB-INF/struts-config.xml. Open this file, and add the lines highlighted in Listing 6.2 at the appropriate places. Listing 6.2: struts-config.xml
All these settings seem a little complicated at first.
65
Forms
Figure 6.2 shows how all the relevant components interact. A Book bean has a form manager (BookForm) and an action class (BookAction). The bean is then referenced by struts-config.xml. The ActionServlet works in the background—we'll look at ActionServlet a little later.
Figure 6.2: Overview of a Struts Form
The configuration comprises three parts: •
: The name attribute defines an alias for the class specified by the type attribute, which must be qualified by the appropriate package names. The alias must be unique. • : This defines an alias for the JSP page. • : The value of the name attribute must match the alias defined by the element for the form class. The path attribute maps a specific request path to a corresponding Action Class. A JSP page is also given for error handling. We are nearly in a position to execute the application. The next step is to create the Action class for the form. Create a new Java class in the Book subdirectory called BookAction.java and add the code shown in Listing 6.3.
Listing 6.3: BookAction.java (first version) package book; import javax.servlet.http.*; import org.apache.struts.action.*; import tools.*; 66
Chapter 6
public final class BookAction extends Action { java.util.logging.Logger logger = LoggerInitiator.getLogger(); public BookAction() { logger.info("BookAction.java"); } public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { logger.info("Start execute(" + form + ") . . ." ); String title = req.getParameter("title"); Book book = new Book(); book.setTitle( title ); req.setAttribute("BOOK", book); return mapping.findForward("bookCreated"); } }
See that you don't use javax.servlet.ServletRequest by mistake. Its methods are available, so there will be no errors. You won't see any warnings either, because syntactically it is correct. But it does not function exactly as we need. BookAction.java does
not use Struts to its fullest capacity. Still, the example illustrates the basic procedure very well and gets us close to the final version. In the later sections, we will improve it, coming back to compare both methods. The next refinement includes debugging information.
There is one more class to create before we can use the BookForm.java form shown in Listing 6.4. Listing 6.4: BookForm.java package book; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.*; import tools.*; public class BookForm extends ActionForm { private Book book = new Book(); private java.util.logging.Logger logger = LoggerInitiator.getLogger(); public void setTitle(String title) { logger.info("setTitle('" + title+ "')"); 67
Forms
book.setTitle(title); } public String getTitle() { logger.info("getTitle()"); return book.getTitle(); } public void setBook(Book book) { this.book = book; } public Book getBook() { return this.book; } public void reset(ActionMapping mapping, HttpServletRequest request) { this.book = new Book(); } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((book.getTitle() == null) || (book.getTitle().length() < 3)) { errors.add("Title", new ActionError("error.book.title")); } return errors; } }
The BookForm class extends ActionForm. The ActionForm class contains various functions that Struts uses to set and read the input data (more details on this class are given in the Struts API documentation). The point of the class is to define tests that determine if the user's input on the form is valid. In this case, if the title was not supplied (getTitle() returns null) or was too short, an error message is added to the ActionErrors collection. The error message is in fact an error.book.title key for the language file. Additionally, we define how the form will be initialized (see Exercise 1). We will go into the details of the get and set accessors a little later. The text for error messages is still missing. Add the appropriate messages to the language files for the error.book.title key.
68
Chapter 6
Error messages If you are only getting the error messages instead of the form, it's possible that an error has crept in somewhere. Check through your code carefully for typing mistakes and if necessary, download the code for this book and compare the code you have written with the version provided in the code download. At last, we can test the form after compiling and reloading. When you compile the book package, you will need to add struts.jar (found in WEB-INF/lib) to the classpath and also the current directory denoted by a single . (period). Check that the title must be supplied, and must be at least 3 characters. Don't forget to also check that valid titles get through.
Figure 6.3: Multilingual Errors on the first Struts Form
Exercise 1 1. Modify the reset() method from BookForm.java so that books are created with default values. What happens after the modification is done? 2. Expand the form and validation logic to accept a new value (say, the author of the book). 3. The code contains various log entries. Examine the Tomcat output and try to get a picture of the sequence in which the classes and methods are called. 4. What happens if you remove the reset() method of the form? 69
Forms
6.2 Struts Form Beans Now that we have gone over the basic procedure, it is time for implementing the real shop. The Book class now requires further properties and we need another bean for authors. Since it is vital for storing a book, we will start with the Author bean. Create a new context called shop4, as a copy of shop3.
6.2.1 The Author Form If you like a challenge, create the author form by yourself before reading further. It will need an ID, first name, and last name field. The ID could be created by the database (by specifying the column as AUTO_INCREMENT), but for our purposes we shall set it manually. Use the download from the book's website to see the complete version of the class. In this section, we only look at the differences to the book form we created. The form for entering author information looks like this: Listing 6.5: The Struts Author Form
One subtle difference to notice is that we're qualifying the keys specified by the tags, such as author.firstname instead of only firstname. This will allow us to omit the getter and setter methods in AuthorForm.java, as Struts will take care of that for us. This is workable only if we don't need these methods to implement any additional logic.
6.2.2 Formatting the Error Message Display Up till now, error messages have been displayed rather primitively without formatting. A very simple option is to embed HTML formatting directly in the language files: error.author.firstname=The first name must [...]
70
Chapter 6
Using tags as block-level tags ensures that errors are clearly visible and appear on separate lines. A better idea would be to use CSS styles defined in a style sheet so that the formatting is consistent and can be easily updated. It also makes sense to display the error messages next to the form field that they relate to rather than in a single block at the top as we did for the book form. You can do this with the property attribute of :
Error messages will be displayed only when required, so that without errors the form looks just as it has up to now. You could use a table to format the form, placing the error messages in a column of their own on the right-hand side, or you could use Struts logic tags to create more complex layouts. Here is a summary of the steps required: 1. Create the base classes Author.java, AuthorForm.java, and AuthorAction.java in a new author directory. Make sure you implement the correct interfaces, and don't forget to compile. 2. Create the two JSP forms with one as the destination page for Struts to switch to when the form is submitted without errors. 3. Create the entries for errors and form labels in the language files. 4. Insert the , , and elements in strutsconfig.xml. It is always a good practice to copy existing elements and modify them. 5. Reload the context. Ideally, restart Tomcat, because it then displays any errors during parsing of the configuration files on the console.
6.3 Database It is now time to look at the database. First we see how to store the records of an author in the database in the "traditional" way, and then we look at the possibilities Struts offers.
6.3.1 Database and JDBC First thing to ask yourself is what gets stored in the database? Creating the Author class to be responsible for loading and storing itself should be simple enough and the implementation would be relatively simple and uncomplicated. Additionally, we could also make the Author responsible for its own presentation in the View layer. This is something we go into later, when we create the shopping cart in Chapter 10. We'll discover some problems with the approach used in this chapter, so you should read Chapter 10 as well before you implement the methods described here. 71
Forms
Another possibility would be to have one central class that manages database connectivity. This is also very easy to implement and high-performance solutions are possible (database connection pooling, etc.). The problem with this method is that the class becomes longer and longer and eventually we have to start dividing it into smaller parts—which results in a hybrid of both methods. First, we'll try with the simple option: Author and Book classes store themselves. This is one of those situations where code-duplication is almost impossible to avoid. Later, we will see another possibility: storage is handled by the Action class (there are a few problems with this method too as we shall see). Listing 6.6: Author.java (with DB) package author; import java.sql.*; import org.apache.struts.action.ActionForm; import tools.*; public class Author { private String name = ""; private String firstname = ""; private int id = -1; private static PropertiesSingleton config; public Author() { config = PropertiesSingleton.getInstance(); } public Author(int id) throws SQLException { this(); this.id = id; load(); } public void setName(String name) { this.name = name; } public void setFirstname(String Firstname) { this.firstname = Firstname; } public void setId(int id) { this.id = id; } public String getName() { return name; } 72
Chapter 6
public String getFirstname() { return firstname; } public int getId() { return id; } private Connection createConnection() throws SQLException { if (id < 1) { throw new SQLException("Please provide an ID!"); } try { Class.forName(config.getProperty("db.db")); } catch(ClassNotFoundException ex) { throw new SQLException("Could not load DB Driver!"); } Connection connection = DriverManager.getConnection( config.getProperty("db.driver"), config.getProperty("db.user"), config.getProperty("db.password")); return connection; } public void construct() throws SQLException { String sql = "INSERT INTO author (id, Firstname, Name) " + "VALUES(" + id + ", '" + firstname + "', '" + name + "')"; createConnection().createStatement().execute(sql); } public void store() throws SQLException { String sql = "UPDATE author SET " + "Firstname='" + firstname + "', Name='" + name + "' " + "WHERE id=" + id; createConnection().createStatement().execute(sql); } private void load() throws SQLException { String sql = "SELECT * FROM author WHERE id=" + id; ResultSet r = createConnection().createStatement().executeQuery(sql); if (r.next()) { name = r.getString("Name"); 73
Forms
firstname = r.getString("Firstname"); } else { throw new SQLException("No author with id=" + id + " found!"); } } }
The store() method updates the current record of the author in the database, construct() creates a new author record, and finally, the load() method loads the values of the author with the corresponding ID. The execute() method is called only when the author details entered on the form meet the requirements checked for by AuthorForm.validate(). Please keep in mind that the JDBC driver must be on the classpath in order to establish the database connection (placing it in Tomcat's common/lib directory has this effect). The createConnection() method connects to the database using the parameters defined in the config.properties configuration file. Here's what these configuration values should look like: # DB Inputs db.driver=jdbc:mysql://127.0.0.1:3306/shop1 db.db=com.mysql.jdbc.Driver db.password= db.user=root
Note that this shows a blank password, which is for obvious reasons not recommended, especially if your MySQL is set to allow access over the Internet. The question of error handling always arises. In this case, the error is simply passed to the next highest layer with throws, where it appears to the user as a stack trace. While this is easy to program, it hardly creates a professional impression. Later on, we'll come back to see how best to handle such messages. Our struts-config.xml now looks like this: Listing 6.7: struts-config.xml (with Book and Author entries)
74
Chapter 6
The author form is now ready to test. To check that the details given on the form are really getting stored in the database, start the mysql console program. Then select the database with the USE keyword (for example, use shop1;), and enter the following query to see the data in the author table: mysql> SELECT * FROM author;
Figure 6.4: SELECT * FROM author
6.3.2 Using Databases in Struts Struts offers a very convenient way to store the form input in the database. (See the Struts mailings list 09/08/2003: What's the preferred way to use databases?) To demonstrate this we'll first make changes to the Book classes and leave the Author code as is. Start by adding the lines that check for errors to BookForm.java:
75
Forms
Listing 6.8: BookForm.validate() public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((book.getTitle() == null) || (book.getTitle().length() < 3)) { errors.add("title", new ActionError("error.book.title")); } if (book.getPrice() 155
Tools and Tricks
--> Shop]]> Copyright © 2005 Stephan Wiesner. All Rights Reserved.]]>
The configuration file for Ant is called build.xml by default. It starts by declaring variables—in this case, the directory name:
A simple task creates a new directory, if it does not exist already:
The various JAR files which our classes require must also be in the classpath in order to generate the documentation. This is defined like so: 156
Chapter 13
The path is assigned an ID, which can be use to refer to the path later on. The path gets the source code, the servlet-api.jar file, and all JAR files from the WEB-INF/lib directory. The Target is commented out. It can be used for compilation of the files, exactly like the BAT file used earlier. The Target for creating the documentation gets the name javadoc. Before it gets underway, init() is executed and the api directory is created. The directories of the base classes are used and parameters are set. So access="private" indicates that even methods declared as private will appear in the documentation. To begin API generation, open a DOS console, place the build.xml described above in the shop8 folder and enter the following command: ant javadoc
The directory api is created and our documentation is generated as shown in Figure 13.2.
Figure 13.2: The shop8 API
157
Tools and Tricks
13.4.3 StrutsDoc This application can be downloaded at http://struts.sourceforge.net/strutsdoc/. To install, copy the JAR files to somewhere on the classpath (such as the lib directory of shop8). Insert the following lines in build.xml: Listing 13.2: StrutsDoc target
The element references the StrutsDoc classes. You must include the location of web.xml in the webxml attribute of . To execute, start Ant like this: ant run-strutsdoc
Figure 13.3: API created by StrutsDoc
158
Chapter 13
Having an API of the configuration files gives us an overview of a complex project. It is also possible to distribute the API, so that other team members can learn about the content without having access to the original files.
13.5 Display Tags The display tag library is an open source suite of custom tags that provide high-level web presentation patterns which will work in an MVC model. The library provides a significant amount of functionality while still being easy to use. [...]Actually the display tag library can just... display tables! Give it a list of objects and it will handle column display, sorting, paging, cropping, grouping, exporting, smart linking and decoration of a table in a customizable XHTML style. http://displaytag.sourceforge.net/
A standard task faced by the web developer is the display of formatted values, mostly in table form. This can be very easily accomplished with Display Tags available from http://displaytag.sourceforge.net/. They are very easy to use and the web site has many instructive examples that will get you up and running quickly.
13.6 Workflow Often a web application needs a sequence of Wizard-like forms. The typical example is order processing, where the user has to enter a lot of information and various information is displayed. In such cases, it is important that customers do not miss any steps in the sequence, such as skipping the payment details form and yet still managing to complete their order. The Workflow extension is one of the ways out there for implementing this: http://www.livinglogic.de/Struts/description.html. All related forms and the forms that must follow them are defined by inserting entries in struts-config.xml.
13.7 Testing: HTTPUnit As Mozart said, when he wrote music, the notes flowed so fluently and flawlessly onto the paper it was as if he were guided by heavenly inspiration. Alas, we ordinary mortals are bereft of this capacity when we write code, and so we must test our software before release. You can learn a lot from the TestFirst Approach (see Glossary). JUnit is a small application for testing Java methods (http://www.junit.org). It is very well suited for testing the Model classes, but unfortunately not for testing Web interfaces. 159
Tools and Tricks
HTTPUnit (http://httpunit.sourceforge.net/) is an extension of JUnit, created to solve exactly this problem. It lets us call pages, fill forms, and click buttons. Naturally, it takes longer to set up a test than it does to quickly click through the application, but once the test is built, it can run automatically. This can be used to "stress test" a web application, or simply to save time when performing a certain test repeatedly. Here is a simple example to demonstrate the procedure. The JAR files you will need, Tidy.jar and junit.jar, are located in the jars directory of the HTTPUnit download. A more advanced example for testing web applications can be found at http://people.freenet.de/javauser/Testing_Struts.pdf. Listing 13.3: Test.java (HTTPUnit) import import import import
com.meterware.httpunit.*; com.meterware.servletunit.*; java.util.*; junit.framework.*;
public class Test extends TestCase { public static void main( String args[] ) { junit.textui.TestRunner.run( suite() ); } public static TestSuite suite() { return new TestSuite( Test.class ); } public Test( String s ) { super( s ); } public void testGetForm() throws Exception { WebConversation wc = new WebConversation(); WebRequest req = new GetMethodWebRequest( "http://localhost:8080/shop8/index.jsp"); WebResponse resp = wc.getResponse( req ); System.out.println(resp + ""); WebLink[] links = resp.getLinks(); for (int i = 0; i < links.length; i++) { System.out.println(i + ") " + links[i].asText()); } /** resp.getLinkWith( "Enter" ); link.click(); resp = wc.getCurrentPage(); System.out.println(resp + ""); */ } } 160
Chapter 13
First, the HTML page is accessed and its content read. All links are found and their content (that is, the referenced page) is read—in our case, only one level deep. The commented-out section shows how to access many links and their contents. The result looks like this: Listing 13.4: Test result C:\x\httpunit-1.5.4\lib>javac -classpath .;httpunit.jar;junit.jar *.java C:\x\httpunit-1.5.4\lib>java -classpath .;httpunit.jar;junit.jar;Tidy.jar Test .Rhino classes (js.jar) not found - Javascript disabled HttpWebResponse [url=http://localhost:8080/shop8/index.jsp; headers= CONTENT-TYPE: text/html;charset=ISO-8859-1 CONTENT-LENGTH: 494 SERVER: Apache Coyote/1.0 DATE: Sun, 26 Oct 2003 09:34:54 GMT SET-COOKIE: JSESSIONID=3E1B907D10C7508EC122DC16D59B5C07; Path=/shop8 ] 0) Enter Time: 0,531 OK (1 test)
It is possible to simulate and test a series of operations in order to parse and analyze the retrieved pages. It is also possible to execute a stress test, in which lots of tests are started from different computers to see how much load the server can handle. One further testing option is a "crawler". It searches a web site following all links; it also searches the retrieved pages for errors. Listing 13.5: LinkChecker.java import import import import import import import import import import
java.io.IOException; java.net.MalformedURLException; java.util.Vector; org.apache.log4j.Logger; org.xml.sax.SAXException; com.meterware.httpunit.GetMethodWebRequest; com.meterware.httpunit.WebConversation; com.meterware.httpunit.WebLink; com.meterware.httpunit.WebRequest; com.meterware.httpunit.WebResponse;
public class LinkChecker { private String startUrl = ""; private int depth = 1; private WebConversation wc; private Logger logger = Logger.getLogger("LinkChecker"); private Vector visited; private int counter = 0;
161
Tools and Tricks
public LinkChecker(String startUrl, int depth) { this.startUrl = startUrl; this.depth = depth; } public void run() throws MalformedURLException, IOException, SAXException { wc = new WebConversation(); visited = new Vector(); WebRequest req = new GetMethodWebRequest(startUrl); WebResponse resp = wc.getResponse(req); visited.add(resp.getURL()); logger.info(resp.getTitle()); WebLink[] links = resp.getLinks(); for (int i = 0; i < links.length; i++) { parse(links[i], 0); } logger.info("Parsed " + counter + " pages"); } private void parse(WebLink page, int currentDepth) { if (currentDepth > depth) return; WebResponse resp; WebLink[] links = null; try { resp = page.click(); counter++; } catch (Exception ex) { logger.error(ex.toString()); return; } if (visited.contains(resp.getURL())) return; try { logger.info(resp.getTitle() + " / " + resp.getURL() + " (" + currentDepth + ")"); links = resp.getLinks(); } catch (Exception ex) { logger.error(ex.toString()); return; }
162
Chapter 13
for (int i = 0; i < links.length; i++) { visited.add(resp.getURL()); if (links[i].getURLString().endsWith("html") || links[i].getURLString().endsWith("xml") || links[i].getURLString().endsWith("xhtml") || links[i].getURLString().endsWith("htm") || links[i].getURLString().endsWith(".jsp") || links[i].getURLString().endsWith(".do") // Struts ) parse(links[i], currentDepth + 1); } } private void checkContent(String text) { if (text.indexOf("error") > -1 ) logger.error("ContentError: 'error'"); if (text.indexOf("exception") > -1 ) logger.error("ContentError: 'exception'"); } public static void main(String[] args) throws MalformedURLException, IOException, SAXException { LinkChecker lc = new LinkChecker( "http://localhost:8080/TestToolPage/left.html" , 3); lc.run(); } }
A crawler begins at the start URL and follows all links. This repeats recursively up to a set point. It is important to set this end-point, otherwise our crawler will get lost somewhere in the Internet. The content of every page may be tested for keywords.
13.8 Different Struts Functions At this point, it would be good to introduce a few of the functions that are used frequently—such as file uploading—but have not been used in the shop.
13.8.1 Switching Languages Determining the language preference of the browser provides an elegant option to greet customers in their native language. But this is not desirable in every case, and a multilingual site should offer the possibility of switching languages manually. This section looks at a useful procedure for solving such a problem.
163
Tools and Tricks
Exercise 2 How would go about implementing this functionality? Take a little time and try it at your own pace. Struts comes with quite a few WAR files. One of them, struts-validator.war, contains a small web application, explaining the usage of the Validator. The explanatory text is multilingual and the user can choose the language. It is worth glancing through the source. At first, the link itself is very interesting: http://127.0.0.1:8080/struts-validator/locale.do?language=en http://127.0.0.1:8080/struts-validator/locale.do?language=fr
It would be natural to assume that we could change this language parameter to set the language. Try out the following: http://127.0.0.1:8080/shop8/locale.do?language=de. We receive a 404 Page Not Found error. The next step is to take a look in struts-config.xml to see what lies behind the link. When we search for locale, we find the following: Listing 13.6: Locale allocation in struts-config.xml . . .
The DynaActionForm has already been discussed, so we will not go over these again here. We have previously used them in conjunction with a form. The definition here too appears to be for a form, but we are presented with only an HTML link. Next, we should take a look into the class described in . You can find it in the src directory of the example application. Looking over the other files in the directory, we see that we have an Action class without a bean. However this isn't quite the case because DynaActionForm dynamically generates a bean for us.
164
Chapter 13
Figure 13.4: Classes in the Validator example application
The execute() method of LocaleAction.java is shown in Listing 13.7. Before you simply copy the class and integrate it into your application, read the copyright information at the beginning of the .java file. Listing 13.7: LocaleAction.execute() public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { HttpSession session = request.getSession(); Locale locale = getLocale(request); String language = null; String country = null; try { language = (String)PropertyUtils.getSimpleProperty (form, "language"); country = (String)PropertyUtils.getSimpleProperty (form, "country"); } catch (Exception e) { log.error(e.getMessage(), e); } if ( language != null && language.length() > 0 && country != null && country.length() > 0) { locale = new java.util.Locale(language, country); }
165
Tools and Tricks
else if (language != null && language.length() > 0) { locale = new java.util.Locale(language, ""); } session.setAttribute(Globals.LOCALE_KEY, locale); return mapping.findForward("success"); }
The following method sets the language depending on the passed parameters: PropertyUtils.getSimpleProperty(form, "language");
The language and country strings refer to the values defined for them in struts-config.xml:
It is tested whether both values are set and have content. This is true in our case because they are given in a link. It does not make any difference that in reality there is no form used; for the beginner, it looks like a get request from a form. Depending on the input, a new Locale object is created and stored in the session: session.setAttribute(Globals.LOCALE_KEY, locale);
Lastly, the page defined for success—in this case, the first page—is accessed. The org.apache.struts.Globals class that is used contains global manifest constants for the entire Struts framework. It is worth looking into the API, where various standard values are specified.
Exercise 3 What happens if the session is 'lost' (for example, through deactivation of cookies and poor use of URL rewriting) during a visit? Does the chosen language remain the same?
13.8.2 Uploading The Files This is definitely a very common technique and one that is very often discussed on the mailing list. The goal is to let the user select a file on their own machine and then upload it to the server, and display a suitable message upon receipt. First, the code from the actual documentation:
The result is rather unpleasant: org.apache.jasper.JasperException: /StrutsFunctions.jsp(15,0) According to the TLD, the action attribute is mandatory for the form tag.
166
Chapter 13
This is unfortunately very typical, and only a few examples from the documentation work without any modification. A glance at the mailing list tells us that Struts has an example on uploading in struts-upload.war. As a matter of fact, it actually is a working example, but because it is relatively complex, here we'll go over a simplified version. First, we must create two classes, UploadForm.java and UploadAction.java. We have placed them in a package called struts: Listing 13.8: struts.UploadForm.java package struts; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.*; import org.apache.struts.upload.FormFile; import org.apache.struts.upload.MultipartRequestHandler; /** Adaptation of UploadAction from the Struts download. */ public class UploadForm extends ActionForm { protected FormFile theFile; public FormFile getTheFile() { return theFile; } public void setTheFile(FormFile theFile) { this.theFile = theFile; } public void reset() { theFile = null; } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { return null; } }
For this simple method, no input validation has been included—the validate() method is empty. The code simply reads a file. Listing 13.9: struts.UploadAction.java package struts; import java.io.*; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.upload.FormFile; import tools.*; /** Adaption of the UploadAction from the Struts download. */ public class UploadAction extends Action { java.util.logging.Logger logger = LoggerInitiator.getLogger();
167
Tools and Tricks
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { UploadForm theForm = (UploadForm) form; // retrieve the file FormFile file = theForm.getTheFile(); try { InputStream stream = file.getInputStream(); byte[] buffer = new byte[file.getFileSize()]; stream.read(buffer); stream.close(); HttpSession session = request.getSession(true); session.setAttribute("file", buffer); session.setAttribute("mime", file.getContentType()); } catch (FileNotFoundException fnfe) { logger.severe("FileNotFoundException:" + fnfe); return null; } catch (IOException ioe) { logger.severe("IOException:" + ioe); return null; } //destroy the temporary file created file.destroy(); return mapping.findForward("uploadSuccess"); } }
The file can be read directly from the form: FormFile file = theForm.getTheFile();
Finally, the file is read (it is stored temporarily, and we read individual bytes from the buffer) and stored in the session along with the ContentType. We have used a JSP for the display, even though it contains only Java code and so really it should be a servlet. The code is the same however and as it's more readable in this form, converting it to a servlet is left as an exercise for the reader. Listing 13.10: view.UploadView.jsp
After the appropriate ContentType is set, the file is read in from the session as a byte array and then displayed. A word of caution: if we comment out this part, and uncomment the code towards the end, it would be properly displayed in Explorer but not in Netscape (see Figure 13.5).
Figure 13.5: Display with and without content type
13.8.3 Precompilation A JSP page is compiled into a servlet when it is first accessed. The servlet then handles all requests for that JSP. Once compiled, page requests are handled very quickly, but the initial compilation means the first access takes a little longer. Besides, errors show up on compilation. This can be particularly problematic because there can be errors in a JSP when only the classes it uses are changed and not the JSP itself. A step towards solving this problem is precompilation. Tomcat comes with a precompiler (see the Tomcat documentation), but it is very laborious in use. A small trick shown here achieves the same functionality in a simpler way. Jasper (the part of Tomcat that is responsible for generating servlets from JSPs) can be invoked to compile a JSP, without executing the page. For this, you have to access the URL of the JSP at http://127.0.0.1:8080/shop8/index.jsp?jsp_precompile. 169
Tools and Tricks
This has a few advantages over compilation by simply opening the URL for the JSP: • • •
It can be easily automated. We are not actually executing the page and so it is much quicker if, for example, lengthy database requests are to be carried out. Page parameters do not have to be provided.
Any errors while compiling are displayed. One disadvantage is that it will get compiled only if the JSP is modified. If base classes are modified, then no compilation takes place. Due to this, possible errors are not caught. Precompiler.java is a small script that first deletes Tomcat's work directory to force recompilation of JSPs, and all the JSPs given in configuration files are compiled. If an error occurs, then the script is interrupted and you can test manually where the error is.
Listing 13.11: tools.Precompiler.java package tools; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.Properties; public class Precompiler { Properties props = new Properties(); java.util.logging.Logger logger = LoggerInitiator.getLogger(); public Precompiler() throws IOException { logger.info("Precompiler()"); DataInputStream in = new DataInputStream(new BufferedInputStream (new zileInputStream("resources/urls.properties"))); props.load(in); } public String getProperty(String value) { return props.getProperty(value); } public void delete(File dir) throws IOException { logger.info("Precompiler.delete()"); File[] files = dir.listFiles(); logger.info("have " + files.length + " files"); for(int i = 0; i < files.length; i++) {
170
Chapter 13
if (files[i].isDirectory()) { delete(files[i]); } else { logger.info("Deleting: " + files[i].getAbsolutePath() + " = " + files[i].delete()); } } } public void precompile() throws Exception { logger.info("Precompiler.precompile()"); String server = props.getProperty("server"); for(int i = 1; i < props.size() - 1; i++) { String jsp = server + props.getProperty("u" + i) + "?jsp_precompile"; System.out.println("Loading: " + jsp); URL url = new URL(jsp); url.openConnection().getInputStream(); } } public static void main(String args[]) { try { Precompiler pc = new Precompiler(); File f = new File(pc.getProperty("workDir")); pc.delete(f); pc.precompile(); } catch(Exception ex) { System.err.println("Error:" + ex); } } }
The constructor first reads the Properties file. The classpath is hard coded, but it could be given in the general properties file (see Listing 13.11). The delete() method is recursive (it calls itself again). This is necessary because Java cannot delete directories unless they are empty. The subdirectories are passed to the method and emptied, so that at the end, only empty directories remain. This is essential because otherwise Tomcat would not be able to compile the JSPs (citing an undefined condition). In addition, it saves time. Compilation consists of calling all values of the Properties file and accessing the given page. In order not to have to re-enter the complete URL each time, the server address, together with the context, is defined only once. The keys for the configuration values are simply consecutive numbers. 171
Tools and Tricks
This is very simple to manage but has the disadvantage that in the course of time, values are deleted and you lose the overall view very quickly. Instead of using a loop for processing, you could iterate through all properties leaving out server and workDir. Listing 13.12: resources/urls.properties server=http://127.0.0.1:8080/shop8/ workDir=c:/tomcat/work/Standalone/localhost/shop8 u1=index.jsp #u2=AuthorCreate.jsp u2=view/Customer.jsp u3=view/CustomerView.jsp u4=view/BookCatalogueView.jsp
Exercise 4 1. One of the values of the configuration file is commented out, why? 2. What happens if you call the corresponding URL manually?
Figure 13.6: Output of precompilation
13.8.4 Validation of struts-config.xml and web.xml The configuration files in Struts and Tomcat are XML files. That means they can be tested against a DTD, for instance, the DTD given in the header section of struts_config.xml and web.xml. Try to edit the files in an editor—for example, JEdit, an open source editor—that can validate against DTDs. A good editor doesn't just warn you about invalid declarations, but displays a list of possible values as you type, which definitely helps ease the workload, as shown in Figure 13.7.
172
Chapter 13
Figure 13.7: Auto-complete in JEdit
A trick to improve performance is to download the DTD file and use this local copy, instead of referencing it over the Internet. You will have to modify the header section: . . .
This needs to be changed to: . . .
It is important to watch that the correct path is set for the DTD; it can be absolute or relative. The simplest thing to do is to copy the DTD into its own directory. We go over the use of XSLT files in the next section.
13.9 XSLT Configuration Overview The larger an application, the more difficult it is to keep track of configuration information. Since all configurations are in XML files, they can be easily managed through small XSLT scripts. This subject will just be introduced here. For further details, refer to XSLT Programmer's Reference by Michael Kay (2nd edition, 2001, Wrox Press, ISBN 1-861005-06-7). 173
Tools and Tricks
13.9.1 struts-config.xml Listing 13.13 shows an XSLT script which parses struts-config.xml and outputs the forwards with names and paths. Listing 13.13: StrutsForwards.xsl Struts Forward-Definitionen
| |
The important parts of this stylesheet are described below. First, the display format is set: 174
Chapter 13
A global variable is created and set to the server and context, which makes direct linking to the corresponding page possible.
The script consists of many so called 'templates'. The match attribute defines the pattern to which the template should be applied. These templates can also execute actions, and they can be called recursively—recursion is something that features often in XSLT.
This template will be executed for each tag (using // means they may occur at any level of the document). The body of the template shows what should be used in place of every matching element— here, a table—and calls all children, in this case the forward tags.
This template catches any element not covered by a more specific template. It is empty, so such elements are simply discarded. The usage of this script is very simple. Add the following line to struts-config.xml and copy the XSLT script into the same directory (or include the path):
Now if you open struts-config.xml in Internet Explorer, instead of seeing the XML code, the XSLT stylesheet will be applied as shown in Figure 13.8. Clicking on View Source will show the XML without transformation.
Figure 13.8: XSLT output in Internet Explorer 175
Tools and Tricks
If you want to build a file in some other format, you will have to find a suitable tool to perform the conversion. You may find one of the open source projects useful, which can generate PDF documents from an XSLT transformation. It is available at http://sourceforge.net/projects/swiesnerxmlgui.
Figure 13.9: PDF generation
Exercise 5 What happens when you leave out the empty block? What happens when you leave out the following line?
Try to find the explanation behind it.
13.9.2 web.xml In web.xml, there are also various mappings defined, including the two blocks shown in Listing 13.14. Listing 13.14: Mappings in web.xml action org.apache.struts.action.ActionServlet . . . 2 . . . StartServlet /StartServlet 176
Chapter 13
StartServlet /StartServlet action *.do
Again, these mappings should be displayed in a table, but as they are split into two blocks this is difficult to achieve. Listing 13.15 goes through all the servlet elements, displays them, searches for the element with the same name, and displays it. Listing 13.15: ServletMappings.xsl Servlet Mappings (web.xml)
| | |
The stylesheet stores the name of the servlets in a variable: 177
Tools and Tricks
Then it searches for the corresponding mapping and displays the URL:
Figure 13.10: Displaying the web.xml mappings
13.10 Extending ActionServlet ActionServlet represents the "controller" in the Model-View-Controller (MVC) design pattern for web applications that is commonly known as "Model 2". This nomenclature originated with a description in the JavaServerPages Specification, version 0.92, and has persisted ever since (in the absence of a better name). Struts-API ActionServlet is
declared in web.xml:
org.apache.struts.action.ActionServlet
All Struts-related calls go through this ActionServlet. Here is an example in order to clarify its meaning. There was a project that was really close to completion when, at the last moment, the clients decided that they needed a certain security level, where the user's credentials would be tested before execution of each action. Adding corresponding functionality to each action could have solved this issue, but it would have been an enormous task. An unconventional but elegant solution would have been to use Aspect Oriented Programming (AOP). 178
Chapter 13
The solution that was implemented, however, was to simply extend ActionServlet, solving the problem in few hours, including testing and documentation. Listing 13.16 shows an example (change the default entry in web.xml to use this class): Listing 13.16: MyActionServlet.java import org.apache.struts.action.ActionServlet; public class MyActionServlet extends ActionServlet { public MyActionServlet() { super(); System.out.println("--- MyActionServlet()"); } protected void process( javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { System.out.println("--- MyActionServlet.process_1(" + request + ", "+ response + ")"); System.out.println("--- Request.lastname:" + request.getParameter("lastname")); super.process(request, response); System.out.println("--- MyActionServlet.process_2()"); } }
The process() method is critical. It allows you to implement custom actions on the request and response and execute the normal flow before or after respectively (with super.process(request, response)). A better alternative is the use of servlet filtering. But this is not often possible in older versions of Tomcat. A project that offers this security function is the SecurityFilter Project at http://securityfilter.sourceforge.net/. RequestProcesssor Since Struts 1.1 and the introduction of subapplications, the RequestProcessor class has replaced ActionServlet. Processing for subapplications should be inserted in this class.
179
Tools and Tricks
Summary In this chapter, we have discussed some useful tools and tricks which simplify routine tasks in Struts development and maintenance. As you may expect, a search on the Internet will reveal that there are many more tools available. Have a look and see what's out there, and because many are open source, you can try any that sound useful!
180
A Solutions Chapter 2: Hello Struts Exercise 1 1. How would you go about displaying French text? You must create a new .properties file. This file will be named application_fr.properties. You can have access to the values in this file after a reload. Try this out, adjust the language settings of your browser and call the pages again. 2. How would you use text that spans multiple lines? The properties files are built on a name-value schema. You cannot use a line break here directly. You can insert \n, but it won't be displayed in HTML. Instead of this, you can use HTML elements, for example, . But pay attention that you stay with HTML; it will become complex if you use XHTML. We will come across this in later chapters. 5. It was indicated in the chapter that English is the default language; is this correct? Swap the file names over: change application.properties to application_de.properties and vice versa. What happens now if you try to access the page using a preferred language that isn't supported? Whichever language you set in the application.properties file becomes the default language. This can be done using the file without a language extension (that is, without a language code in the name, like application_de.properties). It will be used if no other suitable file is found, for example, for a Spanish visitor.
Chapter 3: The Struts Shop Exercise 2 How can we store the data in files instead of a database? What advantages and disadvantages would this have?
Solutions
Every good book on databases will contain at least one chapter describing the situation before the development of databases. Spend some time creating the following file: Listing A.1: Orders.txt Wiesner, Stephan; Struts Book; 01.02.2004; 34,99 Müller, Lieschen; Grow Roses; 03.12.2003; 13,98 Beutling, Frodo; Adventure in the wilderness; 03.02.2004; 47,00 Bergsten, Dietrich, Programming for everybody; 05.05.2004
Now try to read the individual rows and columns and sort them. Search in the file for all the orders placed in a certain time frame and within certain price range. Naturally, you have to write a second program to access the file. You will now understand how strenuous this is and will learn to admire a good database. A side aspect is naturally the resource hunger of any database. The Oracle 9 standard installation takes almost 1GB of space on disk. This is unimaginable for an application that runs on Mobile phones. On the other hand, HSQLDB (http://www.hsqldb.org), which is an open-source Java Database, requires only 160 KB of main memory.
Chapter 4: Internationalization and Taglibs Exercise 1 Create a second logo for another language. Change index.jsp to display the logo in the user's language. Be brave: you can mix normal HTML and Struts! See the download for the book. There are two options. Which do you think is cleverer?
Exercise 2 2. What happens if a key of a message bean has no corresponding entry in the language file? It gives out an exception and this is naturally not very pleasant. You have to proceed very careful here. One can easily forget one entry, if there are lots of files to update. 4. How would you implement one more language (for example, Spanish)? How complex would it be? You have to create one more language file and give it a corresponding country code. Copy the default language file and then replace individual values, so that no values are forgotten. 5. Could you allow the user to choose the language explicitly, and if so, how? For example, could you offer a button on the page? The solution is in Chapter 1. 182
Appendix A
6. Think about how you could provide more extensive text in multiple languages, such as a help page. The standard language files do not provide much help here, because they can only handle short texts efficiently. (Hint: Take another look at the first custom action example.) For extensive text, use your own files and a custom action for loading. A file name with country code needs to be provided here. A default file will be loaded for unknown countries. 7. Why shouldn't you work on the WEB-INF/struts-bean.tld file? Think about what would happen if you did add your own settings to that, and then a new version of Struts was released. After extending/expanding the file, you cannot predict what will happen if you change the Struts version. The changes make it difficult to find errors and it increases the work for a developer.
Chapter 5: Logging and Configuration Exercise 1 1. How would you rewrite the Singleton class to create only two, only three or only ten instances? Insert here a HashMap that contains the chosen number of instances. Here is an example: Listing A.2: PropertiesManager.java import java.io.*; import java.util.*; public class PropertiesManager implements java.io.Serializable { /** The singleton reference */ private static PropertiesSingleton config; /** Contains the configurations */ private static HashMap configurations = new HashMap(); private PropertiesManager() { } private Properties load(String configFile) throws IOException { Properties props = new Properties(); DataInputStream in = new DataInputStream( new BufferedInputStream (new FileInputStream(configFile))); props.load(in); return props; } public static PropertiesSingleton getInstance(String configurationName, String configFile) throws IOException { if (config == null) { config = new PropertiesSingleton(); 183
Solutions
} Properties p = config.load(configFile); configurations.put(configurationName, p); return config; } public static PropertiesSingleton getInstance() { if (config == null) {throw new RuntimeException( "No configuration file was provided, yet.");} return config; } public String getProperty(String configurationName, String key) { return ((Properties) configurations.get(configurationName)).getProperty(key); } }
2. It would be nice if configuration files were read automatically at every startup. How can you achieve this? Include a Method reload (String Filename). This file deletes old entries of Properties-Objects and replaces them with the newly read values.
Exercise 4 1. Why does declaring the error statement help us much less in JSP than it would in a normal class? The JSP is compiled into a Servlet. The declared statement refers to this servlet. You have an option to check the Servlet and then draw conclusions from your JSP. 2. Set the file path for Log4J to c:/ instead of c:\. Reload the context. What happens to the log file? Where do you see the error message (if at all)? Unfortunately, the file is not created now and no error message is displayed either.
Exercise 5 1. The LoggerHTMLFormatter class explicitly uses a German date format. How would you change the class to use the server's locale? The following statement is very important: DateFormat dateFormatter = DateFormat.getDateInstance( DateFormat.DEFAULT, currentLocale);
A glance in the Java-API shows us this method: static DateFormat getDateInstance() Gets the date formatter with the default formatting style for the default locale.
184
Appendix A
2. Why does the LoggerInitiator class have an empty constructor? Can an empty constructor be left out? It is important that it's declared as private and not so much that it is empty. If it is left out, then the class will automatically get an empty public constructor, and other classes can evade the Singleton mechanism.
Chapter 6: Forms Exercise 2 1. What happens if you disconnect the database and start the Tomcat? Can you still execute the DataSource statements from struts-config.xml? What happens if you disconnect the database in the middle of executing a statement? Naturally, you cannot execute the statements without the database, an error occurs in this case. If you disconnect the database in the middle of the execution, this will also give an error. Since we are not using a Transaction, it is difficult to decide in which state our data currently is. Transactions should prevent exactly this problem. A good database tries to restore itself to a consistent state. 2. The element in struts-config.xml contains an attribute validate. Set this attribute to False and try entering correct and incorrect details for a book. With the validate attribute set to False there is no test for input fields. In other words, it can send an incomplete form or no form at all.
Chapter 7: Logic Exercise 1 2. Why is the Book.compareTo() method (Listing 7.5) not declared using public int compareTo(Book foreignBook)? If you were to declare it this way, could you leave out the conditional for testing the Book object? Yes, of course, you can do this, but the significance of this method is to overwrite the standard method for comparing. Please take a look at the Quotation at the beginning of the chapter once again. 3. Find a book or web site about sorting algorithms and write a Java console application to test and compare Bubblesort, for example, with Shellsort, for large numbers of objects, say 10 million. It is important to think about extreme cases. The Quicksort is faster than Bubblesort in most cases. But in some cases, it not only requires more space, but also more time. Go over the API for class java.util.Arrays. The Quicksort is used here for sorting. 185
Solutions
Chapter 9: Controller and Templates 2. Create a new JSP with any name and call it through StartServlet. Delete the JSP and try to call it again (without reloading the context). You will see that you can insert and delete a file at run time. This advantage fades away if you have to build logic in the controller, for example, testing for authentication.
Chapter 10: Putting it All Together Exercise 4 3. Consider what is required to allow customers to make changes to their shopping cart, such as increasing or decreasing the number of books. For this, a function must be created. You can do to this in two ways—using a Form and Action and everything that belongs to them or through a Link and a direct call to a servlet. This link then will contain the ISBN number and a + (plus) and - (minus) respectively. The count will be changed as per current visitor. If the quantity reaches zero, the product will be deleted. Subsequently, the shopping cart is refreshed.
Exercise 5 2. You will have noticed the use of return statements to terminate page processing. Why does the page function this way? What is the problem with this approach? See the source code of the generated HTML page and of the generated Servlets in the c:\tomcat\work directory.
3. Prices are again handled as int and must be rounded off. Why not simply use double values and save the effort of formatting? Test a few calculations, and you should be able to spot the problem! Java rounds off. Division will result in absolute value of 100000001 instead of 1. In order to correct this, a corresponding operation must be carried out. See in the API for class java.math.BigDecimal.
Chapter 12: JSTL Exercise 3 1. What happens, if you mistype and write instead of ? Compare that with the following: instead of 186
Appendix A
The old notation gives out a compilation error. The use of JSTL does not give this error. See also the next exercise for this. 2. What are the effects of not treating null pointers as errors, but simply ignoring them? How does it affect the troubleshooting process? You might have sensed that very strong opinions have been expressed about this. Do not just accept it. Find out the arguments against it. It's something the developers from Sun thought long and hard about before choosing this behavior. If you don't have enough related experience, then talk to a veteran who's had to maintain a program with more than ten thousand lines. Ask a Perl guru if they have ever used the strict option and why. Lot of people would have learned programming on the C64 with Basic and considered JavaScript, Perl, and PHP to be their kind of programming languages. But once you learn Java, do some projects, and use server-side JavaScript in complex projects, you start admiring it despite its complexities. This is mainly because the compiler eliminates many sources of errors. With client-side JavaScript, it often happens that one of the branches of an If statement is called only after weeks. To find and correct errors in that particular If statement (which might be just spelling mistakes) at that point of time will be laborious.
187
B Glossary Few, but effective words are enough to instruct. Seneca Apache The Apache Software Foundation is a widely spread group of volunteers responsible for open-source projects around the world. Besides the popular Apache Web server, another JSP-related project by this group that is quite popular and very important is the Jakarta Project. It offers the open-source solutions for the Java platform and the Apache XML project. See www.apache.org, jakarta.apache.org, and xml.apache.org. Application Server The Application Server makes application programs available for the users over the Web. It is also responsible for the execution of database access for a web application. The server can be on the same computer as the Web server or can reside on a different computer. Bytecode This is an intermediate code between a high-level programming language like Java, Smalltalk, Lisp, and so on and machine code executable in a particular environment. It is built by a corresponding compiler. This bytecode contains the instructions for a virtual machine and is platform independent. To execute the bytecode on a particular platform, we need a bytecode interpreter that emulates the virtual machine. CGI (Common Gateway Interface) /CGI Script CGI is a standard that defines the communication of a user with a web server over the Internet. A CGI script is an external application that is executed as a response to a request from a browser to the web server. For example, if a user fills out a form on a website, the CGI script validates the information and passes that information to the database program of the server. CGI script can be written in various languages such as Perl, C, C++, and Visual Basic.
Glossary
Context The subdirectories of the Webapps folder (or any other folder specified in Tomcat's server.xml configuration file) in Tomcat are used to build individual projects. These are called contexts. Cookie Cookies are small protocol files. These can be loaded on the client computer from the web server when the client's browser makes requests to the web server. Cookies can contain information about the visited site, about the user's site preferences, the duration of the visit, or the number of visits. This information is accessible if the website is revisited. The server can retrieve the data contained in the cookie. The browser settings can be changed so that cookies are blocked. DTD (Document Type Definition) A DTD defines the rules for an XML file. It simply lays down the structure and not the content. For instance, the DTD for XHTML specifies that a table column must be inside a table. So the XHTML DTD can specify that a table column must have content (! = empty), but it cannot make sure that the content makes sense. eXtensible Markup Language (XML) XML is a metalanguage for defining the document types. These document types enable an adaptable data format for exchanging structured documents over the Web. XML is a simplified version of Standard Generalized Markup Language (SGML). The International Standards Organization (ISO) gave recognition to SGML as a standard metalanguage, in which one can define markup languages for documents. HTML is developed as a specific document type from SGML. It is a simple markup language with predefined tags. On the other hand, XML has the character of a metalanguage and provides the developer the possibility to define customized markup languages—semidialects—with customized tags for many different document types. The XML 1.0 version has been a W3C standard since February 1998. eXtensible Stylesheet Language (XSL) XSL was standardized by W3C in October 2001 as a language for the formulation of stylesheets. XSL is composed of two parts. The first part is a language for controlling the transformations that can convert an XML source document to another XML document or a non-XML document such as an HTML file. The second part is a vocabulary for specifying the formatting semantics. XSL works with formatting objects and provides the designer with stylesheets that can be used to present the design of the final document in detail.
190
Appendix B
eXtensible Stylesheet Language Formatting Objects (XSL-FO) XSL uses a stylesheet mechanism that is derived from Document Style Semantics and Specification Language (DSSSL) to transform XML documents to a presentable structure. You can control the formatting of a website in such way that it can be transformed on the fly, for example, for display on a digital television. eXtensible Stylesheet Language Transformations (XSLT) One can transform one XML file in another XML file with XSLT. The target format need not be another XML file; PDF and HTML files are also typical targets. We have seen an example in this book that the web.xml file is shown as HTML file. HTTP Response Codes The following table shows response codes that can come up frequently while processing a request: Code
Description
100 Continue
Till now everything is OK, proceed further.
200 OK
The request has been processed successfully; response will follow.
301 Moved
The page is deleted. A "Redirect" will follow.
304 Not Modified
The resource has not been modified and can be downloaded from the browser cache. This is particularly used in connection with pictures.
401 Unauthorized
No authorization, cannot logged in.
403 Forbidden
No authorization, server refusing to fulfill the request.
404 Not Found
Page not found.
500 Server Error
Normally a program failure. The processing was unsuccessful.
Interface An interface is an abstract operation, that is, an operation without implementation and/or an operation that puts together constant class attributes. It makes certain functions available for developers in abstract form. Interfaces can be inherited from other interfaces. Classes implement interfaces. A class can implement many interfaces simultaneously. Java achieves multiple inheritance through interfaces. JavaBeans There are two types of JavaBeans—normal and J2EE (JAVA 2 Enterprise Edition) Enterprise Beans, which are similar to normal beans, but bigger and more complicated. 191
Glossary
The beans discussed in this book mean normal JavaBeans. They are classes in which variables are not called "variables" but "properties". This is because properties make Get and Set methods available for the variables. As per convention, these methods are named exactly like the properties, but with get or set prefix and the first letter a capital. This has lot of applications and that's why they are adopted in Struts and JSTL. The following is an example of a bean: public class MyBean { private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
J2EE (Java 2 Platform, Enterprise Edition) J2EE is component model for the development of enterprise applications that are based on open standards and makes available standardized technologies such as JDBC, JNDI, JCA, JSP, and EJP. The access to the critical services such as transaction management, security service and persistence are implied in J2EE, which simplifies the application development process. J2ME (Java 2 Micro Edition) This is a Java Technology for mobiles and other embedded products. Since it is designed for various types of products, it works with configurations and profiles. A profile defines a set of APIs for a certain family of products. A configuration defines a minimum set of class libraries and a virtual machine that is available for a certain device type. Jakarta Project It is a project of Apache Software Foundation that provides the open-source components for the Java Platform. Tomcat, the famous servlet and JSP container, libraries such as JSTL, and frameworks like Struts fall under this project. See jakarta.apache.org for more information. Java Archive (JAR) The JAR is a platform-independent file format that allows an integration of different components in compressed form. The jar tool from the JDK by Sun is used for packing and unpacking files. The archive contains a manifest file with meta-information about the archive, file signature, and so on. Java Server Pages (JSP) Similar to the older Active Server Pages (ASP), JSP allows the creation and execution of dynamic and interactive web applications. HTML tags, text, and special JSP elements can be combined in a JSP, which then is executed from a server. Unlike ASP, JSP 192
Appendix B
applications are not dependent on Windows Server. The JDBC driver is used as an interface for accessing the database. Java Applet This is a Java program that is inserted in a website and can be executed by a browser that understands Java, either automatically once the website is loaded, or when activated by the user. Initially, applets were used for small animations and multimedia effects, but now they are also adopted, for example, for interactive model-calculations and also for games. JAX JAX, the web tool API for XML in J2EE, allows us to write a web application completely in Java. The APIs are divided into two major groups: those that deal directly with XML documents and others that deal with procedures. The document-oriented Java API for XML Processing (JAXP) processes XML documents with the help of parsers. Java Architecture for XML Binding (JAXB) generates the corresponding classes for Java as elements in a XML schema. Java API for XML Messaging (JAXM) sends SOAP messages over the Internet. Java API for XML Registries (JAXR) offers a standardized way to access various types of registries like UDDI or ebXML. Java API for XMLbased RPC (JAX-RPC) sends method calls with the help of SOAP in order to access Web tools that are available over the Internet. JDBC (Java Database Connectivity) JDBC, similar to ODBC, is a standard for accessing databases with Java. JDBC allows implementations for all major databases, and because it is written in Java, it is platform independent. The advantage of using JDBC lies in its portability over the databases. It is often possible to change from Oracle to MySQL; all you need to do is copy a JAR file and insert the link in the configuration file. Naturally, this can be easily done only with the functions that are supported by both the databases. In reality, database-specific functions creep in the programming, making the exchange very laborious. Mapping Values are mapped to other values. It is typical to allocate the alias names in web.xml and struts-config.xml. action *.do
Model-View-Controller (MVC) Design Model The MVC implies that an application has a Model, a View for that model, and a Controller. The Model part of the application contains the application logic, for example, the access to the database or certain calculations. The View and Controller represent the user interface, which is divided into input and output components. The Controller is an 193
Glossary
input component that provides the application logic with the information it has received. The View is the output component, which displays the information that has been sent by the application logic.
Properties Java offers functions (java.util.Properties) for loading and storing name-value pairs in configuration files. The name and the value are then separated with an = (equal) sign. Comments can be started with a #. This mechanism is used, for example, for multiple languages. According to conventions, the files end with country code.properties. So, for example, the file used for German values would be resources_de.properties. You can load these values with: Properties props = new Properties(); DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream(configFile))); props.load(in);
Request/Response HTTP is a stateless protocol. There is usually no connection between two requests (sessions can be used to provide such a connection). A request contains a wide range of information about the client, such as preferred language, the browser in use, and the operating system. A response contains a wide range of information about the server, such as the name of the server and its version. Requests usually are sent as GET or POST (there are also HEAD, PUT, and DELETE). A normal URL is a GET; it can have parameter, for example, from a form (http://localhost:8080/index.jsp?user=Stephan), but is limited to 250 symbols. Mostly GET has been used in this book, so that the parameters can be read, which helps in debugging. POST is used for forms. The length of the passed parameters is not limited, and the values are not displayed in the URL by the browsers. Here is an example of a Request-Response-Result (Internet Explorer and Tomcat Server): GET /shop HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */* Accept-Language: de Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Host: localhost:8080 Connection: Keep-Alive 194
Appendix B
HTTP/1.1 404 /shop Content-Type: text/html;charset=ISO-8859-1 Content-Language: de-DE Transfer-Encoding: chunked Date: Sat, 25 Oct 2003 12:01:10 GMT Server: Apache Coyote/1.0
A suitable tool to display this information is IEHttpHeaders, which can be downloaded from http://www.blunck.info/iehttpheaders.html (only for Internet Explorer). The structure of the response requires that you can set the information in it (for example, setting the error code) and the cookies only as long as there is no content written. As soon as you start with sending the real site/page (via out.println() statements), you can no longer do it. Following is a simple JSP that displays the entire Header information: Listing B.1 Header.jsp
Servlet The Java Servlet Technology provides a process to create dynamic web content. Its main advantage over the previously used CGI Scripts is that it allows performance improvement; it is also platform-independent. Similar to other J2EE components, a servlet also runs inside a container implemented through J2EE platform. This container makes the interaction of the servlet with the client possible and makes the predefined environment of different Java tools available. Singleton A Singleton (Design Pattern) is a class in which only objects are defined (usually only one). They are very well suitable for maintaining scarce resources and simplifying for example the synchronized access to these resources. In this book, a Singleton maintains the configuration data. A singleton is loaded only once from the file, creating a single instance that is accessible from all classes of the project.
195
Glossary
Simple Object Access Protocol (SOAP) SOAP is an XML-based message format. Using this, applications can communicate over the Internet in a certain pattern independent of the platform, object model, and programming language. The messages are packed in an envelope and can be directly connected with the application through a firewall. SQL (Structured Query Language) SQL is a standard query language for relational databases. The statements are similar to English sentences, making it very easy to use. For example: SELECT Name, Firstname FROM Customer WHERE ID=1234
The above statement will display the name and first name of the customer having the ID 1234 or NULL if there is no customer with that ID. Template A template is a sample edit format. In Struts, the option used to define the layout of an application is called as a template. Test First Approach The Test First Approach is an integral part of Extreme Programming (XTreme); it corresponds with the idea of Kent Beck of carrying out the well-established "Best Practice points". There is still debate over effectiveness of this approach. There are experiments, for example, with pair programming (two developers sharing one computer), which at least in a lab setting proved that there is definitely an improvement in the performance. Beck advises first writing all the tests for all possible problems before even starting developing the software. The software development will be carried out till all tests are successful. This idea has to be implemented in order to understand it. The fundamental problem is that the programmers cannot test their own software. Why? Because they think their software functions smoothly. When its time for testing, they are looking for acknowledgement, rather than errors (and doubt their own competence). In this approach, before even the development process begins, problems need to be solved. This is an intellectual challenge, which has to be addressed with full energy. What can go potentially wrong? What happens with null input and what can lead to an exception? These considerations alone boost the stability. If all these considerations are put into tests, it improves the maintainability of the software. These tests are called Unit tests and they are executed after every modification. So it is very easy to find out that modification in class A has caused error in class B. This kind of 196
Appendix B
error is very difficult to find out by simply using error messages. For further information about unit testing, refer to http://www.junit.org. Transactions Transactions make sure that each result of a database access is considered as unit. If a transaction fails, then all actions are rolled back. They avoid any inconsistent data. Transactions are not supported by all databases. It is interesting to see the problems with distributed databases. WAR file WAR files are JAR files with a different file extension. They are created in a similar way (using the jar command of JDK) and normally contain a complete context. Tomcat can unpack one of these files automatically and start the web application contained in it. XML Definition Language This is the XML language for defining XML schemas. It became a W3C standard in May 2001. The language contains a wide range of elements that allow the construction of data models and definition, and derivation of data types in cases where a wide range of data types are provided. XML Declaration The first line of XML documents can and should be an XML declaration as a processing statement. It makes the document immediately recognizable as an XML document for a processor. Besides the XML version, the character set used in the document is also declared. In addition, the standalone attribute is also declared to indicate if the document has an internal entity or is linked to external files, for example, an external DTD. A typical example is:
XML Document This is a document that is well formed for the purpose of XML reference. An XML document could be a valid or invalid document that may or may not correspond to a DTD or XML Schema. A document is physically composed of entities. The logical structure of an XML Document is composed of information units such as elements, comments, and processing statements.
197
Glossary
XML Processor An XML processor works with an application such as a web browser like Internet Explorer. It is software that reads an XML document and provides access to its content and structure. XML Path Language (Xpath) Xpath is a language that enables addressing parts of an XML document. Xpath uses an address syntax that refers to a "tree of nodes". This tree of nodes is built on the structure of a document. The localization expressions, built in a similar way to file paths, are used by XSLT and also by Xpointer. XML Schema An XML schema is a formal regulation of the structure of a particular class of documents. The XML schema defines the elements and attributes for a valid document instance. It also defines the structure of the elements. At the same time, an XML schema, unlike a DTD, can give precise information about the data types that limits the content of the elements and the values of attributes. Another difference between a DTD and an XML schema is that an XML schema is itself a XML document; so an XML processor can also process it.
198
C Literature 1. The Cathedral and The Bazaar, Eric S. Raymond: O'Reilly, 1999 (ISBN 1-56592-724-9). 2. Effective Java, Joshua Bloch: Addison-Wesley, 2001 (ISBN 0-201-310058). 3. Design Patterns, Gamma, Helm, Johnson, Vlissides, Addison Wesley, 1995 (ISBN 0-201-63361-2). 4. JavaServer Pages, Hans Bergsten: O'Reilly, 2000 (ISBN 1-56592-746-X). 5. Java Internationalization, Deitsch, Czarnecki: O'Reilly, 2001 (ISBN 0-596-00019-7). 6. Software Process Improvement: Metrics, Measurement and Process Modelling, v. 4 Software Best Practice, Haug, Olsen, Bergman (Eds.): Springer Verlag, 2001 (ISBN 3-540-41787-7). 7. Java Servlet Programming, Jason Hunter: O'Reilly, 1998 (ISBN 1-56592-391-X) 8. XSLT, Michael Kay: Wrox, 2001, 2nd Edition (ISBN 1-86100-506-7). 9. Extreme Programming Explained: Embrace Change, Kent Beck: Addison Wesley, 1999 (ISBN 0-201-61641-6).
Index A Action class 8, 13 ActionErrors 97 ActionServlet 13, 66, 103, 178 Ant 199, 155 Apache 189 Apache Jakarta-Projekt 5 API 158, 159 application.properties 8, 28 Applicationsserver 189 Author.java 74 Authorisation 111 Autocomplete 173
B Bean 42, 63, 64 Book.java 64 Book.jsp 64 BookAction.java 67, 77 Bookcatalogue 120 BookCatalogue.java 86, 121-123 BookCatalog.jsp 87 BookCatalogueView.jsp 123 BookForm.java 68 Bookstore 31 Browser 114 Business-Logic 17 Bytecode 189
C central control 103 CGI 189 CGI Script 189 classpath 45 code conventions 153 collection utilities 81, 84 Comparable 85 compareTo 85, 86 Configuration 51
Configurationsfile 74 ContentType 169 Context 190 Control 99 Cookie 114, 116, 190 Core 143 CountryDate 48 CreateAuthor.jsp 108 Custom Actions 44, 81 Customer.java 117, 118 CustomerForm.java 118, 119 Customer registration 32
D database mapping tool 5 database 71 data source 75 debugging 18, 54 definitions 106 design patterns 10 design guidelines 13 development environment 42 display tags 159 Document Type Definition See DTD DTD 172, 173, 190 DuplicateKeyException.java 94 DynaActionForm 127 DynaBeans 14
E Eclipse 154 EL, Expression Language 143 Environment variable 24 Exception Handling 92 Exceptions 91, 92 Expandability 35 eXtensible Markup Language 190 Extensible Stylesheet Language 190 Extreme Programming 196
F Filestructure 27 fmt.tld 146 Footer.jsp 108 form 8, 63 form Beans 70 frameworks 7
H
L layout 104 Log4J 59 LogFile 56 logging 51, 54, 59 logging mechanisms 51 logic 81 login 127 login entries 54 log information 141
Header.jsp 109 HTTP 113, 114, 191 HTTPUnit 159
M I
ID 157 Installation of Struts 26 Interface 191 Internationalization 28, 39, 40 143, 145 Introspection 14 Iteration 31, 147
J J2EE 192 J2ME 192 Jakarta Commons Logging 61 Jakarta project 192 JAR archive 192 JAR files 135 Java Database Connectivity See JDBC Java Server Faces 39 Java Server Pages See JSP Java API generation 155 Java applet 193 JavaBean API 14, 15 JavaBeans 9, 11, 191 Java classes 146 javadoc 157 JavaScript 139 JAX 193 JDBC 14, 34, 71, 193 JDK logging 55 JEdit 173 JSP 12, 33, 106, 140, 192 JSTL 81, 143, 148, 152
202
Mailing list 38 Mapping 246 Mapping-Error 89 McClanahan 20 minlength 174 Model 2 26, 52 Model 23, 107 Model 2 concept 51 Model View Controller 25 Model 2 architecture 52 Model classes 23 Model-View-Controller See MVC Multilingualism 12, 57 MVC 25, 26, 246 MySQL 49, 53, 100
N Netbeans 41
O Order 132
P packages 9 password 35 performance 54 PHP 6 plugin classes 135, 141 precompilation 169, 172 precompiler 169
Precompiler.java 170 PriceFormat.java 125 primary key 36 Properties 194 Properties file 171 PropertiesSingleton.java 54
R redirection 81, 83 reflection 14 reload 29 request 194 response 194 reusability 17 root 35
Struts legacy 78 Struts project 6 Struts shop 31 Struts templates 99 Struts Validator 135 sub-application 103 substring matching 81, 83 swing 32
T
S servlet 195 ServletMappings.xsl 177 servlet specification 9 session 15, 114-116 session length 16 session management 32 shop 39 shopping cart 49, 126, 130 simple expression language 143 Simple Object Access Protocol see SOAP singleton 51, 195 SOAP 196 sorting 85 SQL 143, 196 SQL statements 120 StartServlet 102 StartServlet.java 100 stateless protocol 113 Structured Query Language See SQL Struts 146 Struts console 153 Struts EL 149 Struts framework 7 struts-bean.tld 48 struts-config.xml 66 StrutsDoc 155, 158 Struts forms 66, 69 Struts functions 163
tables 35,36 taglibs 11, 39, 44, 183 see also Custom Actions taglib.tld 46 TagUtils 49 tag library 143 template 99, 104, 196, Test First approach 196 tester 19 threads 17 Tiles 104 TilesDefinitions.jsp 107, 108 tiles-defs.xml 107 Tomcat 23, 52, 111, 112 Tomcat, error page 91 Tomcat, standard installation 24 tools 153 torque 5 transactions 197 tricks 153
U User inputs 135
V validation 172 validation.xml 137 Validator 8, 135, 140 value comparison 81 view 81
W WAR file 26, 197 web.xml 176
203
Welcome page 42 workflow 159 WSAD 154
X XForms 140 XML 144, 190 XML Path Language 198 XML schema 198
204
XML schema Definition Language 197 XML declaration 197 XML document 197 XML files 136 XML processor 198 XPath 198 XSL 190 XSLT 173, 175, 191
Thank you for buying Learning Jakarta Struts 1.2: a concise and practical tutorial Packt Open Source Project Royalties When we sell a book written on an Open Source project, we pay a royalty directly to that project. So, by purchasing "Learning Jakarta Struts 1.2: a concise and practical tutorial", Packt will have given some of the money received to the "Apache Software Foundation" project. In the long term, we see you and ourselves—customers and readers of our books—as part of the Open Source ecosystem, providing sustainable revenue for the projects we publish on. Our aim at Packt is to establish publishing royalties as an essential part of the service and support a business model that sustains Open Source. If you're working with an Open Source project that you would like us to publish on, and subsequently pay royalties to, please get in touch with us.
Writing for Packt We welcome all inquiries from people who are interested in authoring. Book proposals should be sent to
[email protected]. If your book idea is still at an early stage and you would like to discuss it first before writing a formal book proposal, contact us: one of our commissioning editors will get in touch with you. We're not just looking for published authors; if you have strong technical skills but no writing experience, our experienced editors can help you develop a writing career, or simply get some additional reward for your expertise.
About Packt Publishing Packt, pronounced 'packed,' published its first book "Mastering phpMyAdmin for Effective MySQL Management" in April 2004 and subsequently continued to specialize in publishing highly focused books on specific technologies and solutions. Our books and publications share the experiences of your fellow IT professionals in adapting and customizing today's systems, applications, and frameworks. Our solutionbased books give you the knowledge and power to customize the software and technologies you're using to get the job done. Packt books are more specific and less general than the IT books you have seen in the past. Our unique business model allows us to bring you more focused information, giving you more of what you need to know, and less of what you don't. Packt is a modern, yet unique publishing company, which focuses on producing quality, cutting-edge books for communities of developers, administrators, and newbies alike. For more information, please visit our website: www.PacktPub.com.