Struts Tutorial
Struts Tutorial
Hands on with
Examples
Preamble
Are you a Mechanic or a Driver?
Some people wake up in the morning and go to their garage. Then they roll under the car to tweak the engine. They worry about four-stroke versus rotary, the plug timing and cycles. Other people also wake up in the morning and go to their garage. Then they drive the car to work and look down the highway. They tend not to worry what cycle the engine is in at the moment, the exhaust or the fuel injection system. In programming, there are also two kinds of people: system programmers and application programmers. System programmers write code that is used and valued by application programmers. System programmers write frameworks or DAO implementations. Application programmers, however, write for end usersto provide business value. They leverage other peoples work so that they can focus on the application and not the mechanics. When writing applications, it is more effective to leverage a framework and other reusable JARs and components than to reinvent the wheel This book shows you how to build quality web applications at dramatically increased speed cost-effectively with the open source Jakarta Struts framework. The Jakarta Struts framework, a project of The Apache Software Foundation (www.apache.org), provides a sound application architecture and dramatically reduces the time it takes to write web applications that deliver value to the business. Struts is based on published standards and proven design paradigms, such as the Model 2 approach, a variation of the well-established Model-View-Controller (MVC) architecture (in use since 1970s) and is compatible with Suns Java/Java 2 Enterprise Edition (J2EE) platform.
Copyright ASF
3 Together with Java Standard Tag Library (JSTL), Struts brings Java Servlets, JavaBeans, Java Server Pages (JSP), Extensible Markup Language (XML), and message resources into a unified framework.
Best Practices
Anyone can build a bridge or a web application. But by knowing (and applying!) best practices, an engineer can build web applications optimally: best quality, the fastest, the cheapest. The authors have seen development projects fail miserably when architects and developers mistakenly took the excellent J2EE blueprints as their only guidance. Therefore, this book does not cover everything that you could do with J2EE and Struts. The authors rather expose practices that they have found to work best in real life to build web applications that deliver the highest value to the business in terms of quality, speed and cost. For example, this book addresses issues such as how to increase development productivity by maximizing reuse of beans, actions and events, how to increase reliability of the application by the choice of software and hardware, and the importance of sound requirements specifications and project planning for cost-effective development that meets client needs. Fools ignore complexity, experts avoid it; geniuses remove it. (Alan Perlis The authors do not claim to be geniuses; they have simply tried to keep the book simple and short by concentrating on the approaches that work best in practice and still cover the complete project cycle. The following are 12 best practices which contribute to producing quality web applications speedily and cost-effectively. They are presented in the following chapters: 1. Meet client needs efficiently via sound requirements specifications.
Copyright ASF
4 2. Save money and increase reliability with open source and effective hardware. 3. Develop iteratively with a proven process. 4. Unit test. 5. Apply MVC( Model-View-Controller). 6. Use OO (object-oriented programming) to maximize reuse. 7. Use DAO. 8. Use CRUD Events. 9. Use CMA. 10. Stress test. 11. Use Q/A and manage releases.
Hands on
This book wants you to be immediately operational, so it relies heavily on tutorial exercises. To really benefit from this book, better do not skip the labs as they help you understand and gain confidence in the demonstrated technologies. Once you have completed the book and the labs, you may wish to test your new competencies on basicPortal CMS (Appendix A) which has been built using the best practices illustrated in this book. basicPortal CMS is a dynamic, Struts-based portal application with content management that combines the functionalities and features that are required for about 80% of all web projects. It thus allows you to concentrate on features that are unique to your web project. BasicPortal includes support for e-commerce/credit cards, news, lead tracking, content syndication, fora, calendars, web logs (blogs), wikis, email support, high-speed standard J2EE security, row-based security, images, blobs and uploads.
Why Struts?
To speed up development, you can acquire a proprietary framework, but you may find that the vendor does not allow you to open the hood to change the oil. Jakarta Struts is open source and does allow you to open the hood. You have control over the source code and can change it yourself to build a web application that fully meets your clients needs. Besides, Struts offers more advantages for both developers/programmers and for your clients business: Major developer benefits:
Copyright ASF
5 Runs on top of any J2EE application server, including IBM WebSphere, BEA WebLogic, Oracle IAS, Borland, Novell exteNd, Tomcat, Resin and Orion. Extremely solid and stable architecture (communitys code contributions increase stability). Ease of use, yet suitable for large-scale data-driven web applications. Breaks complex applications into simple, consistent sets of components. Ensures all developers in the team develop code using the same approach. Complete documentation (users and developers guide). Encodes best practices as many users help debug and improve open source code. Large developer community provides virtually instant support. Major business benefits: Speedy and cost-effective development. Substantially reduced time-to-market and quicker product releases. Open source: license is either free or almost free. Freedom from closed, proprietary systems, thus no costly licenses to keep track of, renew and upgrade. No dependence on specialist labor and vendors. Numerous features meet a variety of business needs. Platform-independent. Greatly modular.
Copyright ASF
6 If you are part of an inhouse, multidisciplinary IT team, it may be wise to also understand the taks and responsibilities of others in the team, such as the project manager, the system analyst, the architect, technical support staff, etc. If a project fails, all too often the blame is put on the developer(s)/programmer(s), so in the interest of the profession in the long run and in your own interest, it cant hurt to be competent and able to suggest better approaches, for instance, to insist on a complete requirements specification prior to development, recommend a choice of software or advocate for an effective project management technique. This book is great for a team effort. Feel free to compare notes with your colleagues. If you are or will be participating in a large project, building and working on the basis of a common understanding of technology can do wonders for project success.
To benefit from this book, you should already have an understanding of the ModelView-Controller paradigm (MVC). Figure 1-1 shows the MVC2 architecture that we will use with Struts and JSP. You should be aware of the request-response process and terminology: (1) The Client Browser posts a request to the Controller servlet. (2) The Controller servlet dispatches to an Action class. (3) The Action positions the Model (i.e. retrieves, updates, inserts, deletes data). (4) The Controller then forwards to a JSP page. (5) The JSP extracts the positioned data from the Model. (6) The Controller returns the JSP content as a response.
Copyright ASF
7 You should know what JSPs are and how they work. You should also have some experience with the Java Servlet API, HTML, SQL and JavaScript. The more Java programming experience you have, the better. In an ideal world, you will have already participated in developing a Java application that has made it into production. Quickcheck 1: On the Internet, go to http://java.sun.com/products/servlet/2.3/javadoc/index.html. Are you familiar with the API? Quickcheck 2: Do you understand the following HTML fragment? <FORM ACTION=http://localhost:80/myServlet> First Name: <INPUT TYPE=TEXT NAME=firstName><BR> Last Name: <INPUT TYPE=TEXT NAME=lastName> <BR> <INPUT TYPE=SUBMIT> </FORM> If the answer is no to either question, the authors strongly recommend that you do some prior reading or find a team member that will bring you up to the required level.
Copyright ASF
8 IMPORTANT: All designs and software is copyrighted, and designs are patented, specifically basicPortal data model, its applications and base classes are patented, but available under a flexible license.
Copyright ASF
Table of Contents
Preamble..............................................................................................................................2 Are you a Mechanic or a Driver?....................................................................................2 Best Practices..............................................................................................................3 Intellectual Property and License...............................................................................7 Table of Contents............................................................................................................9 Part I The Foundation ...........................................................................................................................................16 Chapter 1: Project Setup....................................................................................................17 Software Requirements.................................................................................................17 Hardware andOperating System Configuration...........................................................18 IDEs and Code Generators............................................................................................19 I......................................................................................................................................19 Production Environment...............................................................................................20 Popularity of Open Source............................................................................................21 Reliability of Open Source Software............................................................................21 Support for Open Source Software...............................................................................22 Tomcat 5....................................................................................................................22 Testing Sample WARs..................................................................................................23 Creating a Web Application Project in the IDE...........................................................23 Classpath...................................................................................................................24 Application Server Configuration.................................................................................26 Review.......................................................................................................................26 Lab A: Install/Unzip..................................................................................................27 Lab B: Deploy Sample WARs..................................................................................28 Lab C: IDE Setup......................................................................................................29 Chapter 2: The Requirements Specification.....................................................................30 Reporting with iReports................................................................................................32 Return on Investment (ROI).........................................................................................32 Chapter 3: Database Access Setup ..................................................................................34 Data Base ......................................................................................................................34 PostgreSQL 7.5.........................................................................................................35 Designing the Model.....................................................................................................35 Creating an Entity-Relationship (E/R) Diagram..........................................................35 JNDI DataSource Configuration...................................................................................36
Lab E: SQL Install....................................................................................................37 Lab F: Model Design ...............................................................................................39 (Optional) Lab G : Output/reports............................................................................40 ...................................................................................................................................40 Chapter 4: Using Simple Actions ....................................................................................41 The C in Model-View-Controller..............................................................................41
Copyright ASF
10 Using Action Mappings................................................................................................41 3 Rules for Structuring Action Mappings.....................................................................42 Writing an Action Class................................................................................................43 Configuring the Struts Action Servlet..........................................................................43 Chapter 5: Using JSPs and View......................................................................................46 Integrating JSP Tags.....................................................................................................46 JSP 2.0 and JSTL1.1.....................................................................................................47 Localization...................................................................................................................47 Layout With Tiles.........................................................................................................49 Creating Tiles Definitions.............................................................................................50 Using Tiles Forwarding................................................................................................51 CSS............................................................................................................................51 You may already want to follow the OASIS/Portlet CSS naming conventions to avoid unnecessary rewriting of JSPs should in the future you wish to make application modules fully JSR-168- or WSRP-compliant. The WSRP specification is available for free and without registering at the WSRP TC website of oasis-open.org. The Portlet specification is available at www.sun.com .......................................................................................................................................52 Skin-able Style and Branding.......................................................................................52 Menu Navigation...........................................................................................................53 JavaScript..................................................................................................................54 Review.......................................................................................................................55 Lab I: Layouts & Navigation....................................................................................56 Lab J: Skin-able Branding Style..............................................................................58 Chapter 6: Using Simple Beans........................................................................................59 What is a Java Bean?.....................................................................................................59 Form Example...............................................................................................................59 What is a Form Bean?...................................................................................................60 Realistic Bean Prototype...........................................................................................62 Writing IDE Macros......................................................................................................63 When to Map a JavaBean.........................................................................................64 Logging.....................................................................................................................66 Review.......................................................................................................................67 Lab K: Simple Form Beans and Macro...................................................................68 Chapter 7: Development Process Tips .............................................................................70 Can You Build a Bridge (or a Web Application)?.......................................................70 What is Project Planning?.............................................................................................73 What are tasks?.........................................................................................................75 Iterative process steps...................................................................................................77 Coding to Contract........................................................................................................78 Whom to hire?...............................................................................................................78 Exit Strategy..................................................................................................................79 Fixed Bid ..................................................................................................................79 Project Politics ..............................................................................................................79 Review.......................................................................................................................80
Copyright ASF
11 Preview..........................................................................................................................80 Lab L: HTML text tag...............................................................................................81 ...................................................................................................................................81 Part II The Bricks and Mortar ...........................................................................................................................................85 Chapter 8: Doing Data Access Right................................................................................86 Introduction...................................................................................................................86 Data Layer Access Approach The M in MVC..........................................................86 Sample DAO Interface..................................................................................................87 Mapping a DAO to the Database With SQL................................................................89 Connecting a DAO With the Data Source Pool...........................................................90 Using the DAO..............................................................................................................91 What Makes a Good Programmer?...............................................................................92 Object-disoriented Programming..................................................................................92 The Benefits of Object-oriented Programming............................................................93 Making DAOs Reusable...............................................................................................93 Using DAO Helper Objects .........................................................................................94 Review...........................................................................................................................96 LAB M: DAO...........................................................................................................97 Chapter 9: Reusable Beans ...............................................................................................99 Unit Testing...................................................................................................................99 Populating the Bean ...................................................................................................100 JSP and Bean...........................................................................................................103 MVC For Form Beans With DAO..............................................................................104 Editing Data ................................................................................................................105 Reusable FormBean....................................................................................................105 Common Bean Methods..............................................................................................105 Iterator Adapter...........................................................................................................106 Review.....................................................................................................................106 Chapter 10: Reusable Actions and Events ....................................................................109 Messages From Page to Action..................................................................................109 DISPLAY TAG.......................................................................................................111 The Display tag library is a most popular and advanced tag library library for data grids. It allows you todo multi-page, sortable data grids. You should review the display tag documentation and examples available on the Web. ..............................111 Debugging JSP Request and Session .........................................................................111 First Cut Action Dispatching .....................................................................................111 Drill down...............................................................................................................113 Events..........................................................................................................................114 Action Event Object using Context............................................................................115 Extending the Action..................................................................................................116 Action Class With Events...........................................................................................118 Review.....................................................................................................................119
Copyright ASF
12 LAB O: More Simple Actions...............................................................................120 LAB Q: Advanced Action /w Save Event..........................................................123 Chapter 11: More Form Handling Action.......................................................................126 Alternative MVC Flow Options ................................................................................126 * Forward................................................................................................................128 Insert Event.................................................................................................................128 Exception Handling.....................................................................................................129 Exception Flow...........................................................................................................129 Multi-row Update........................................................................................................131 Transactions................................................................................................................132 Master-Detail Processing............................................................................................133 Bookmarkable action..............................................................................................134 One way to do this is to go via a tile definition mapping like this:.......................134 Review.....................................................................................................................134 LAB S: Multi Row Update...................................................................................136 LAB U (Optional): On New.................................................................................137 Chapter 12: Validation....................................................................................................138 Validation Messages...................................................................................................140 Displaying Validation Errors......................................................................................141 Client Side Validation.................................................................................................142 Business Rules........................................................................................................143 Error page................................................................................................................143 Duplicate submit.....................................................................................................144 Review.....................................................................................................................145 Preview........................................................................................................................146 LAB V : Validation.................................................................................................147 -- Brian Kernighan..........................................................................................................149 Part III Perspectives.....................................................................................................................150 Chapter 13: Dynamic Site Content ................................................................................151 Dynamic Content........................................................................................................151 Content Administration...............................................................................................153 Implementing Content Search ....................................................................................154 Mapping Search SQL..................................................................................................155 Form Bean for Search Results....................................................................................156 JavaScript tree.........................................................................................................157 Asynchronous Processing.......................................................................................160 e-Commerce and XML...............................................................................................161 Generating Ad-hoc Reports .......................................................................................162 Optional LAB AA : Dynamic Content...................................................................165 LAB AX Opt: Report Servlet.................................................................................166 Chapter 14: Security ......................................................................................................167 Login Security with Container-managed authorization.............................................167 Configuring Container Security..................................................................................168
Copyright ASF
13 Membership ............................................................................................................169 Implementing Row Level Security.............................................................................170 Adding Row Level Security to Bean and SQL Map..................................................170 Dealing with Inherited Rights.....................................................................................172 Tiles and Menu Security.............................................................................................173 SSL..............................................................................................................................174 Geocode User IP.....................................................................................................174 Other Security.........................................................................................................175 Review.....................................................................................................................175 Chapter 15: Complex Forms and Dot Notation..............................................................177 Complex Forms...........................................................................................................177 Nested Beans...............................................................................................................178 Dot Notation................................................................................................................179 Nested Tag...................................................................................................................180 Review.....................................................................................................................181 Optional LAB AC: Nested Dot Notation...............................................................182 Chapter 16: Dropdowns and Super Type........................................................................184 Using Option Collections for Drop downs ................................................................184 Obtaining Option Values from the Database..............................................................186 Super Type Implementation........................................................................................187 SQL-map and DAO for Super Types.........................................................................187 Review.....................................................................................................................188 * (Required) LAB AD : Options Select..................................................................190 Chapter 17: Rich UI........................................................................................................191 XML-RPC...................................................................................................................191 Serving Content as XML............................................................................................192 Case: Accessing Struts Beans from Excel .................................................................193 Flex..........................................................................................................................194 Flash............................................................................................................................195 This schema above illustrates how the Data Access Components in a Flash application communicate with a remote server using XML via HTTP.................197 Creating Components..............................................................................................197 Blob upload.................................................................................................................198 Software as Utility..................................................................................................198 Review.....................................................................................................................199 LAB AF Opt: Flash 2003 ......................................................................................200 Chapter 18: Managed Performance ...............................................................................201 Operation Monitoring.................................................................................................201 Loading Data...............................................................................................................202 Stress Testing..............................................................................................................204 Database Server...........................................................................................................205 Application Server .....................................................................................................205 Co-Location.............................................................................................................206 JDBC DataSource...................................................................................................206
Copyright ASF
14 SQL Query Execution Process...................................................................................207 How Database Engines Execute Queries....................................................................207 How Database Engines Treat Joins............................................................................208 Stored Procedures........................................................................................................209 More SQL Tips...........................................................................................................210 Fail over ......................................................................................................................211 Review.....................................................................................................................211 LAB AH Optional: Stored Proc..............................................................................212 Chapter 19: Making Your Applications Future-Safe.....................................................214 Release Engineering....................................................................................................214 Change Management...................................................................................................215 Ant and Maven best practices.....................................................................................215 CVS.............................................................................................................................216 Fail over Recovery .....................................................................................................217 Which application server to Use?...............................................................................217 Chains Filter ...............................................................................................................218 Design..........................................................................................................................219 What is Overrated.......................................................................................................219 Common Mistakes......................................................................................................219 Appendix A.....................................................................................................................223 Appendix B Tomcat.................................................................................................236 Appendix C- Linux Scripts ...................................................................................237 Appendix D Jdate Time.......................................................................................239
Copyright ASF
15
Fools ignore complexity; experts avoid it; geniuses remove it. Alan Perlis
Copyright ASF
16
Copyright ASF
17
Software Requirements
This book uses standard programming techniques that work with any choice of servlet engine, application server, SQL database and IDE. To complete the tutorials in this book, you need the following: A Java application server/container (JSP 2.0) such as Tomcat 5 An SQL database, such as pgSQL An integrated development environment editor (IDE) such as Eclipse A 1.4 Java Virtual Machine (JVM) such as J:Rockit Commonly needed JARs, such as for Struts 1.2, JSTL 1.1, Data Access Object (DAO) implementation (such as iBatis). You could download and install these by yourself. However, configuring a complete environment with IDE and application server speedily and that works smoothly together can be time-consuming. The books tutorials and sample code, therefore, use Jasic, an integrated development suite that the authors have created to enhance your projects cost-effectiveness and that has already shown to work well together. Jasic provides enormous productivity gains and allows you to concentrate your resources on features that are unique to your project. Jasic, named after Jakarta and basic, leverages several of The Apache Foundations Jakarta Struts projects into a comprehensive set of features and functionalities for web application development. Its components are either pure Open Source or free for development and testing purposes at the time of releasing this book. Jasic includes the following popular components:Tomcat 5.x and Resin 3.x Application Server (for development) PostgreSQL 7.4 database with pgAdminII administration tool
Copyright ASF
18 Eclipse 3 IDE with SolarEclipse web applications plug-inJava Virtual Machine 1.4 (non Sun)Struts 1.2, JSTL 1.1, iBatis DAO implementationbasicPortal CMS best practices sample codeiReports Visual Report Designer Jasic can be downloaded from the Web by following instructions available at http://www.basebeans.com and http://www.infonoia.com. The components provided with the Jasic suite are for the Windows 2000, NT, and XP operating systems. All components are, however, also available for Linux, so you could create a complete environment for Linux instead. A cheat sheet with links to further resources is maintained at http://www.basebeans.com. Feel free to use your favorite IDE in parallel with the tools provided with the Jasic suite, and explore the differences and advantages of either. The authors further recommend that you download and install a Netscape or Mozilla browser. Some HTML tags that work in Internet Explorer (www.microsoft.com) may not work in other browsers, so it is best to test on a variety of platforms. The mentioned browsers are available for download at http://www.netscape.com and http://www.mozilla.org. Finally, make sure that you are using a recent Java Development Kit (JDK). You can test this by typing the following at a command prompt:
java version
Copyright ASF
19 autoload when you reboot the machine. For example, rename MSIM.exe to MSIM_bak.exe. You can then still load it when you need to. Virus detection software which runs in the background can also be detrimental to developer productivity. Mostly, virus detection software is necessary to avoid execution of macros in the Outlook or Outlook Express mail programs which are tightly integrated with the operating system. If you use a mail program such as Netscape Messenger which does not execute macros, you may be able to disable the virus checker without having to worry too much about getting infected. Java is cross platform which gives you the freedom to choose the best operating system to meet your needs. If you deploy your application to a Unix system, consider making Linux your operating system for development. Linux uses little system resources and can therefore be comparatively fast on a given hardware, which reduces the cost of development. Also, the stability of Linux may allow you to avoid having to re-boot your machine several times a day to free up system resources. The book labs are Windows based, but developers who use Linux should be able to pick up on the instructions.
Copyright ASF
20
Production Environment
The cost of production operations is an import factor for larger commercial applications. Departmental users have traditionally accepted runtime licensing costs for application server, portal server and the SQL engine. The operations cost for commercial applications that run in order to generate profits must be scrutinized very closely. These commercial applications typically require a rack of servers running in a cluster. If the total license cost was $5,000 (USD) per machine, and there are 192 machines in the rack, you end up paying one million USD just in license fees. For this kind of money, you may in theory be able to develop your own SQL database software rather than license one and still come out on top. The licensing costs are much higher than $5K per CPU. Oracle DB, MS Advanced Server, Application Server, Portal Framework, etc. Under the open source model you obtain a license which is either free or almost free. This removes licensing cost as an issue when choosing the best software for your application. In addition, you have access to the source code. Examples for open source software are Linux, OpenOffice, PostgreSQL, Eclipse, Struts and basicPortal. Open source will be discussed in more detail in the next section. When choosing hardware, good places to start are http://www.tpc.org andhttp://rubis/objectweb.org/results.html. The latter body rates cost per transaction. An operations environment for a commercial application will typically have many application servers with about 2 gigabytes of RAM each. But it would typically also have only one database transaction server or one database cluster; maybe one more for reporting and/or staging and failover. This is because replication between several databases can be detrimental to performance. Since you are typically limited to one database server, you want to make sure it has maximum memory, such as anywhere upwards from 8 or 16 gigabytes of RAM. Higher memory allows the database to cache more indexes, and thus perform faster. Up to around 80 per cent of the cost of an application can be in production, so by carefully choosing the production environment you may well be able to help your organization save some real money and be more profitable.
Copyright ASF
21
Figure 1-1: Popularity of Open Source Figure 1-1 shows how the open source Apache server software has increased in popularity since the year 2000. One can almost predict when it will achieve a 90 per cent market share.
Copyright ASF
22 spend large amounts on migrating or rewriting applications because commercial ventures turn out not to be viable or decide to drop support, but do not provide the source code so the organization could maintain the application itself. Vendors also try to lock you into their support, which could be insufficient.
Tomcat 5
Tomcat is a standard servlet container that has average (doesnt sound so well) performance, in many cases it is faster than commercial containers. It works best as a front end, itself answering port 80, no container should be fronted with legacy Apache or IIS. Tomcat and other containers can serve both HTML pages and JSP pages. A container like Tomcat provides services like session tracking, data source connection pools, logging, security, etc. Some people integrate Tomcat with Axis so they can use SOAP web services. Tomcat has built in single sign on, so that a user authenticated in
Copyright ASF
23 one WAR does not need to sign in if they go to another WAR. It also comes with a load balancer. You can deploy a WAR file by placing it in a web apps folder and restarting the container. When placing a container in production, you want to configure the JVM settings.
Copyright ASF
24 One level down from this project root is the application root. Example:/jasic/myproject/myapproot. The application root folder will contain the items that will be included in the WAR that is to be deployed. Often you do not wish to deploy the source code, just the class files. Such class files would typically go to C:/jasic/myproject/myapproot/WEB-INF/classes. In most cases, the file structure inside /myapproot will mirror the structure of your WAR. This is not a must: advanced editors such as Eclipse allow you to create WAR structures that may be different from the file system layout. They allow you to include files that are possibly shared with another project. Eclipse 3 The Eclipse workbench is an open source rewrite of Visual Age for Java. Eclipse runs in Linux, OSX and Windows. The first thing you need to do is create a project. You can do that by clicking New/Project. While coding, you will note that it has code completion features. Also, you can just click on a class or a method and hit F3 to drill down into the code. Eclipse has great support for CVS. It also has a lot of plug ins, such as one that allows you to run Tomcat in Eclipse. A favorite feature is that it allows you to write macros: Preferences/Java/Editor/Templates will make you more productive. You should not need to use ANT builds in day to day development. You know that you are configured right if you can change action, bean or view classes, refresh the browsers and see the effect of new code.
Classpath
During deployment, the class path should be blank. Just by placing a WAR file to webapps, the container knows how to handle it. During development, your IDE needs to be configured to include all the JAR files in the lib folder.
Copyright ASF
25 Example project:
Most containers have an auto-deployment feature which allows you to test code changes without having to package and deploy the WAR every time. These mechanisms can be great time savers because you do not have to go through the deployment step each time when running or testing your code. These mechanisms typically assume that all files needed in the project are inside the application root (//jasic/myproject/myapproot). Here is how to set up Tomcat server.xml, for example, to see your project:
<Context path="" docBase="c:/jasic/bPproj/bP" reloadable="true" > </Context> </Host where the folder /bP is your web application root (myapproot).
Copyright ASF
26
Review
You should have setup an application server and editor. You should also know how to find independent rankings of price performance of application servers. You must know how to get support and ask questions for open source tools.
Copyright ASF
27
Lab A: Install/Unzip
1. TStart with installing a sample development environment that includes IDE and application server. If you have a slow Internet connection, get a CD. If you have a fast Internet connection, such as DSL or cable, it could take one hour to download. You can download the integrated environment as recommended. If you want to or are using Linux, you can download each component individually. Download Jasic.zip. 2. Unzip the files to C:\ 3. Using the environment variable, set JAVA_HOME to your java location. Type in java version. Make sure your system environment path is simple and set to: %JAVA_HOME%\bin;%SystemRoot%\system32;%SystemRoot% 4. To have JDK 1.4 or higher on your machine, do a search for java.exe in all your hard drives and rename those folders to *.bak. Ex: SunJava1.3.BAK 5. Make sure you do not have memory resident programs running on your machine, then press CNTRL-ALT-DEL. For higher productivity, do not use Outlook/MS Internet Explorer, but Netscape, for example. You may disable the virus scanner, provided you do not violate a company policy. Note: Your machine should have more than 256 MB of RAM to be responsive. 6. Optional: Review the downloads listed on the downloads page of baseBeans.com Review links listed on the cheat sheet page of baseBeans.com. Print and study the iBatis DAO and JSTL documentation (both are links in the abovementioned cheat sheet or download page). Review windows installation instructions for pgSQL.
Copyright ASF
28
Copyright ASF
29
4. Optional: review struts-config, server.xml, resin.config, etc. using a text editor (Vi is provided, do not use notepad) Do not run it yet, it needs a working DB, etc. for JDBC pools/Realms.
Copyright ASF
30
Copyright ASF
31 One of the authors had been brought in by a client to recover their project which risked to be delayed by another consulting firm. The client said they were to build an employee data entry screen and that they had a requirements specification document. When the mock-up was presented, the client was very unhappy; that was not what they asked for. Of course, the requirements specification documents were designs, not requirements and data input is not a requirement it does not show how the system will get used. While the author socialized with the client, he found out that they wanted to report bank teller fraud to the FBI for prosecution! In fact, what they wanted was an application that outputs all the forms that make it easy for the FBI to arrest a teller that steals. They also had to track the status of this paper work. Of course, after that analysis of real client needs, it was easy to build the application that did this for them. When they saw the new prototype, the client was thrilled.
Copyright ASF
32
Copyright ASF
33
Lab D: Mockup
1. Start up an html editor of choice. (Vi, TopStyle, Netscape, Eclipse) 2. Create a file TaskEdit.html (you can use a similar name for the file that you like). Create this file in \jasic\bPproj\bp. (same folder that contains WEB-INF). Requirements! Think of what a task html form should have on it? It should have fields that are used to edit/update a task. For example taskName, Status, AssignedTo, description, a few more fields that you think a task tracking software should have. Be creative.. 4. You could hard code values of the fields. For example: <td> Task Name : </td> <td> Create an army of clones </td> etc. Create a 2 file taskList.html (again a similar file name would do ).
nd
In it create only taskName and maybe 1.. or 2 more fields, repeating one under each other. <tr><td>Create an army</td></tr> <tr><td>Attack the closest star ship</td></tr> etc. This is your mock up. 5. Start up application server. 6. Using a browser, navigate to: localhost\taskName.html and then localhost\taskList.html. You can start up IDE (Eclipse) to touch up the files if needed. Note that your container has or should have lines like this that points your editing enviroment as /: <Context path= docBase=/jasic/bPproj/bP debug=0 reloadable=true crossContext=true> </Context> </Host>
Copyright ASF
34
Copyright ASF
35
PostgreSQL 7.5
The database pgSQL is known for its high volume transaction support and stability. It offers significant cost savings on staffing and the authors find that it has better support than proprietary vendors. It supports full ANSI SQL, including stored procedures, and full Joe Celko designs, such as correlated joins, etc. www.sf.net is an example of a large site based on pgSQL.
Copyright ASF
36
<parameter> <name>maxActive</name> </parameter> <parameter> <name>password</name> <value>changeme</value> </parameter> <parameter> <name>removeAbandoned</name> <value>true</value> </parameter> <parameter>
For Resin resin.conf, the following would have the same effect: <database> <jndi-name>jdbc/bppool</jndi-name> <driver type=org.postgesql.jdbc3.Jdbc3ConnectionPool> <user>bpuser</user> <password>changeme</password> <serverName>localhost</serverName> <databaseName>basicportal</databaseName> <max-connections>3</max-connections> </driver>
Copyright ASF
37
</database> You can test your pool via a simple servlet such as this: public class XServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse res) { PrintWriter out = null; Connection conn = null; try { out = res.getWriter(); res.setContentType("text/html"); Context ctx = new InitialContext(); if (ctx == null) throw new Exception("Boom - No Context"); DataSource ds = (DataSource) ctx.lookup("java:comp/env/bppool"); if (ds != null) conn = ds.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from usrs"); if (rs.next()) { foo = rs.getString(2); } //if stmt.close(); conn.close(); out.println("DB Con. data is: " + foo);
Copyright ASF
38 2. Install a db admin tool, such as pgAdmin (there are others, such as EMS manager). In pgSQL folder you can right click on pgadmin file to install it 3. Using the admin tool, create a db, call it bp. 4. Using the admin tool, create a user, call it bpuser with a password of change me. 5. Using a file manager, locate the create table sql script in (fX) docs folder. Review the sql, (tables created, permissions, etc.). 6. Using the db admin tool, access the created db. Click on the new bp db. In pgAdmin, there is a sql tool (icon looks like a 3 headed monster), click on it. Paste in the create SQL script. Click execute. 7. In docs, there is also a sample data script, execute that as well. Review Tomcat server.xml and resin.conf files. Notice how the db connection data source pools are configured. Configure the connection DS Pools 8. Optional You can now connect to this db using. Excel, or any ODBC tool (PowerBuilder) Also, you could use Squirrel SQL client (or other JDBC tool) to connect to it using JDBC and be cross platform. The optional step is to connect to the DB using Eclipse jFaceDB plug in. - Right click on Connection to create a new connection. - Now you can explore the db in Eclipse as well. Mostly you will be using the pgAdmin tool
Copyright ASF
39
3. Execute the SQL script 4. Using the DB admin tool, enter at least a few rows of data. The more data the better. 5. Using the DB admin tool, issue a sql query. Ex: Select * from tasks where task id = 2 6. Optional: Do explain sql, so you can see the plan on how the query will be executed. This would be important for scalability down the road. 7: Optional: Connect to db using a JDBC tool (SQUirel client or jFace)
Copyright ASF
40
Copyright ASF
41
The action path attribute is what we call the action within the browser. In the example, this may be a URL like http://localhost/do/contentAdminAct. The action type attribute is the implementation class for the action being called. The action class will implement some business logic, such as populating a bean, or validating data. Based on the outcome, the action class decides where to forward to. In the example, the action forwards to a list of contents via contentAdminLst.jsp which the user should see. However under certain circumstances (i.e. user has requested to edit an item), the action forwards to the detail data entry screen contentAdminEdit.jsp . The action class can use any of the forwards defined in the XML, in this case Success, Edit and Saved. If we want more places to go to, we just add more forwards. A forward will either go to a JSP, or to another action. Typically, there is only one action that calls a specific JSP. In the example, there will be no other actions that go to contentAdminEdit.jsp than /contentAdminAct. If another action wishes to also display contentAdminEdit.jsp, it should forward to /do/contentAdminAct. This means it has to call the controller.
Copyright ASF
42
c t o r
t r o
l l e r
J S
P
2
1a ?
J a v
e a n n e P x gt
Action mappings allow you to dispatch to any action class you want and to forward to any JSP or to any other action URL. This gives you a lot of freedom, and if you are not careful, you could easily end up with a jungle of mappings that follow no discernable pattern. Knowing how to use the controller right makes 80% of a successful Struts application. Typically, a page should have one principal action mapping, such as /mypageAct, per page. You can consider this action mapping the controller of that page.
Copyright ASF
43 Then there are three simple, yet important rules to follow: 1. Always call the controller to get to a page. 2. When submitting from a page, submit to its controller. 3. An action forward maps to where a controller can go: to its JSPs or to another controller. If these rules are not clear to you yet, they will become clearer as you continue reading. In the future, whenever you get confused about your application flow, refer back to these important rules. I sometimes say that if you know this, then you know 80% of Struts.
This is what will get executed when the action mapping is called. The most important part is the return of the forward that is mapped. Also, when we first write an action we have a debug message sent to the console. This way we make sure that the action is really called and that the mapping is correct.
Copyright ASF
44
<url-pattern>/do/*</url-pattern> </servlet-mapping>
This means that the Struts framework will be invoked when the container receives any request (URL) that matches the /do/* pattern, such as in http://localhost/myapp/do/myAct. The servlet will then examine the remainder of the URL and call the appropriate action as defined in struts-config.xml. In this case, the action mapped as /myAct would be called. You do not have to call it do you can call it mo or whatever. Review: You know how to structure Struts actions and action mappings
Copyright ASF
45
public class XXAct extends Action { public ActionForward execute(ActionMapping mapping, ActionForm formBean, HttpServletRequest req, HttpServletResponse resp) throws Exception { System.out.println(I am in action for XYZ); // or use log.info if you are expert } return (mapping.findForward(Success)); }//exec
Call the one that lists TaskAct and the one that Edits TaskEditAct. Make sure action mappings point to these actions. 9. Now start the container. 10. Using a browser, navigate to localhost/do/tasks and then do/taskEdit. Look at the output of the console, did you get the message: I am in action . . . That means you called the action right. Always have a message that you called the action right first, so you know you are calling the right action. 11. Did it return the expected page? If you change the struts-config, you will have to restart the container. 12. Once you have it working on Tomcat, shut it down. Start up Resin and browse to the 2 actions. 13. Optional: Modify log4j properties in source properties to include your log output. Ex: log4j.category.com.myOrg.tasks=DEBUG The com.myOrg.tasks should map to your package name that you want to set a debug level to. Note that you should change the log4j file in source folder. Any changes that you might make in the classes folder will get overwriten the next time you do a build. Changing the log4j file in /classes will have it overridden next time you compile. System.out is a bad practice and you should use log4j ASAP.
Copyright ASF
46
<%@ taglib prefix=display uri=/WEB-INF/lib/displaytag.tld %> <fmt:setBundle basename=properties.messages_en-US scope = session var = loc /> <html:errors /> <html:form action=/userJoinAct > <table border=0> <tr> <td class=text_label ><fmt:message bundle=${loc}/>: </td> <td class=form><html:text size=25/> </td> <td class=text_label><fmt:message bundle=${loc}/>: </td> <td class=form><html:text size=10/> </td> </tr> </table> <html:submit property=Dispatch value=Save/> key=user.lastName property=lastName
key=user.firstName property=firstName
Copyright ASF
47
</html:form>
The authors recommend that you read the specifications document for the JSTL tag library JavaServer Pages Standard Tag Library, Version 1.1 available at the www.sun.com The above examples declares the taglibs according to JSTL 1.0. To use JSTL 1.1, web.xml and the taglib declaration URLs have to be modified; see the specifications document for details.
Localization
The above JSP fragment used FMT tags to obtain the content of data labels from a properties file. Using the FMT tags allows you to avoid hardcoding text content in JSPs. You can specify which properties file to draw from with FMT tags as follows:
<a href ='?lang=hr'>Hrvatski</a><br> <a href ='?lang=en'>English</a><br> <fmt:setBundle basename='properties.messages_en-US' scope = 'session' var = 'loc' />
Copyright ASF
48
<c:if </c:if> <c:if </c:if> <fmt:message key="user.lastName" bundle="${loc}"/> test="${param.lang == 'en'}"> /> <fmt:setLocale value = 'en-US' test="${param.lang == 'hr'}"> />
This assumes that your project contains a properties file at WEBINF/classes/properties/messages_en-US.properties . which will likely have been created in src/properties/ . This way, if we want to change a text message that is to be displayed throughout several pages, we have one place only where to make the change. With this mechanism an application can be maintained much more easily. Using resource bundles also allows you to localize your application easily. You could add another file such as messages_de.properties for labels in German. By specifying the locale as de, the labels will display the labels from that file instead. Rather than specifying the locale in each JSP, you would likely set and modify the locale variable (which is available anywhere in the session) on a central page. Your page should not have any text labels on it, it should be completely localized. This is sometimes called word oriented programming. In the file properties/messages_xx.properties, you would have the following:
#user user.firstName=First Name user.lastName=Last Name
In order to localize some Asian languages, you also have to set the encoding using JSTL. To localize messages you want to set in action you can do this:
//import org.apache.commons.resources.*;
public void execute() { Locale locale = new Locale("en","US"); Messages messages = Messages.getMessages( "properties.messages"); String msg = messages.getMessage(locale,"user.firstname");
Copyright ASF
49
}
Both view localization and action localization require properties files such as this:
user.firstName=First Name All the properties for each language are duplicated in files that end in en-US, etc.
Note how the <tiles:insert ...> tag is used to create placeholders. HTML tables, columns and rows are used to define the structure of such a template page.
Copyright ASF
50 The combinations of runtime values for the attribute key of each placeholder are specified in layout definitions. Definitions specify which actual sub-pages to insert when building a page on the basis of a layout. Not unlike struts-config.xml, these definitions are stored in an XML file, such as /WEB-INF/config/layouts.xml. You activate the tiles feature by configuring it as a plug-in in struts-config.xml, and adding the tiles tag library to /WEB-INF/web.xml. The following is an excerpt of strutsconfig.xml:
<struts-config> ... <plug-in className=org.apache.struts.tiles.TilesPlugin> <set-property property=definitions-config value=/WEB-INF/config/layouts.xml/> <set-property property=moduleAware value=true/> </plug-in> </struts-config>
As usual, make sure that the tag library definition file (.tld) exists at the taglib-location you specify.
Note how the definition named mypage2Def extends baseDef. This means that it will have the same header, leftBar etc. and will use the same layout template as baseDef; the only difference is that it will have a different body.
Copyright ASF
51
CSS
One way to use CSS is to use name a style name for each cell like this:
<td class="text">
You could create a skin2.css with different look and feel. In general, most people tend to support only the newer browser standards, to reduce complexity. One way to write up a good style sheet is to manually develop one web page, and based on it create a CSS. The rest of the pages would then use class ids from the style sheet. All look and feel should be in a style sheet and each type of a td cell should have a CSS class associated with it. Sometimes, people add CSS styles for JavaScript browser events to give it a more dynamic look, by you change the properties tag style. You should spend time making you site look nice. There were previously no standards for naming CSS classes; however, the OASIS Web Services for Remote Portlets (WSRP) standard 1.0 proposes a naming standard for
Copyright ASF
52 components of portal applications (portlets). The JSR-168 Portlet standard reuses the OASIS naming proposals. You may already want to follow the OASIS/Portlet CSS naming conventions to avoid unnecessary rewriting of JSPs should in the future you wish to make application modules fully JSR-168- or WSRP-compliant. The WSRP specification is available for free and without registering at the WSRP TC website of oasis-open.org. The Portlet specification is available at www.sun.com
Copyright ASF
53 hoose> type=text/css> ... On a personalized settings page, you could offer the list of stylesheet choices in a Struts select tag as follows: <html:select onchange=document.forms[0].submit(); property=userCss> <option value=choose>Choose your skin</option> <option value=skin1.css>Classic</option> <option value=skin2.css>Modern</option> <option value=skin3.css>Funky</option> <option value=defaultstylesheet.css>Default</option> </html:select> The form action would then have to retrieve the userCss property from the form bean and store it in the session. Look and feel of the application is very important. When the client says the application is good, they usually mean that it looks good, and not that you used Struts.
Menu Navigation
In many web applications, navigation code is found all over the place, and thus very difficult to maintain. The Struts-Menu plug-in with struts-menu.jar and struts-menu.tld offers a standardized and easily maintainable way of creating a menu navigation. Struts menu tags allow different presentation styles for menus, such as horizontal, vertical, based on security, drill-down, using Javascript, etc. Like Tiles, the Struts Menu is a plug-in defined in /struts-config.xml, with the TLD in /WEB-INF/web.xml. The following is an excerpt from struts-config.xml: <struts-config> ... <plug-in className=com.fgm.web.menu.MenuPlugIn> <set-property property=menuConfig value=/WEB-INF/config/navigation.xml/> </plug-in> </struts-config> Displaying Menus A JSP to display a struts menu could look as follows: <% taglib prefix=menu uri=menu %>
Copyright ASF
54 <menu:useMenuDisplayer name=Simple > <menu:displayMenu name=myMenu1/> </menu:useMenuDisplayer> According to the plug-in set-property, the menu structure would be defined in /WEBINF/config/navigation.xml. This would be an excerpt of navigation.xml: <Menu name=myMenu1 title=Resources> <Item name=ref title=Cheat Sheet toolTip=Cheat Sheet page=/do/cmsPg?content=CHEAT_SHEET/> <Item name=downloads title=Downloads toolTip=Downloads page=/do/cmsPg?content=DOWNLOAD/> </Menu> See how it contains a list of items that we want to display, the items, names and what URL to go to. The name of the menu corresponds to what we use with the menu:displayMenu tag to get the items rendered. A number of versatile menu dis players are provided with the struts menu library. In the above example, the menu displayer Simple has been used. So the steps are: 1. User navigates the menu 2. Menu calls the action 3. Action forwards to tile definition Note: Struts logic and bean tag are planed for deprecation, so we do not use them, they are replaced by JSTL tags. We do use display tag later in the book. Example of a struts tab menu:
JavaScript
The following is an example for using JavaScript for client-side procession of the User Interface_
<FORM NAME="aForm"> <INPUT TYPE = "button" NAME="aButton" onClick='myinfo()'> </FORM>
Copyright ASF
55
<SCRIPT LANGAGE ="JavaScript">
function myinfo() { alert("Browser is " + navigator.appVersion + " " + navigator.appName ) document.getElementById("aButton").value="Done!"; }</SCRIPT>
In the event of a user click, it pops up an alert. You can access the one form element like this: document.getElementById(aButton). This is known as a browser DOM. You can do a lot of sophisticated things using DOM. Use the current DOM 1 or DOM 2, which is not the same as Level 0 DOM or the intermediate proprietary DOMs that should not be used. You should make your pages look nice and dynamic and use the client side browsers CPU for UI. Using the browser DOM allows you to reduce the load of the server and makes your application more scalable.
Review
You should now know how to use JSTL, Struts Menu, Tiles, Localization and encoding. You should know that Look and Feel are important.
Copyright ASF
56
Copyright ASF
57 Note: In later labs, if you have problems with the JSP (ex: forward error), you will have to change the mapping back to jsp until the bugs in JSP are fixed; and after it works, revert the mapping back to layout definition). 7. In navigation file in config, edit mMain1 menu. 8. Add a menu items to go to /do/tasks Ex:
<Menu name=mMain1 title=Task List page=/do/tasks/> title=baseBeans>
9. Test.
Copyright ASF
58
Copyright ASF
59
Form Example
The following is an HTML form with Struts JSP tags:
<html:form action=/userEdit > <tr> <td class=text_label> <fmt:message key=user.id/>: </td> <td class=form_input> <html:text property=userId size=10 /> </td> </tr> <tr> <td class=text_label> <fmt:message key=user.userName/>:
Copyright ASF
60
</td> <td class=form_input> <html:text property=userName size=40 /> </td> </tr> </table> <html:submit property=Dispatch value=Save/> </html:form>
Copyright ASF
61 You may have noted that the form beans signature for get/setUserId uses String rather than Integer. Since HTML forms always return a String to the server, Struts expects getters and setters using String. A conversion to other types that may be required in the database (Such as Integer or BigDecimal for ID fields) must be done inside the getters and setters. This type requirement is specific to Struts input tags. A special case are check box tags which use booleans in the signatures. Some tags in other tag libraries which are used for display can handle other types, such as <fmt:dateFormat, which can have getters which return Date or Timestamp. If your form bean is shared between different pages (one that displays data and one that updates data), you may have two getters and setters for the same field, one for display binding, and one for update able form binding. This would be an prototype excerpt of such a bean:
public Date getStartDDate() { //.. } public String getStartDate() { //.. } public void setStartDate(String arg) { //.. }
See how there are 2 getters? You may note that since the display-only JSP is not an input form, the bean does not need a setter method for this property.
Copyright ASF
62
Copyright ASF
63
Copyright ASF
64
Depending on how you use a bean, you have to do things a bit differently. When using a bean for updates (formBean), you must map it in the action. Here is R/W example: As step 1, you declare the bean in the <form-beans> section of struts-config.xml.
<form-bean name=myForUpdateBean class=com.basebeans.mypath.MyForUpdateBean/>
As step 2, in the <action-mappings> section of struts-config.xml, you declare that Struts should automatically put the bean into scope of the JSP (and action).
<action path=/contentAdminPg type=com.basebeans.basicPortal.cms.ContentAdminAct name=myForUpdateBean scope=session validate=false> ... </action>
Copyright ASF
See how Struts passes it to you as an argument? You just cast it as your type and you can use it. It is a mistake to initialize a bean that is mapped, since struts creates it for you. Of course, you do not have to create separate bean classes for display and for update. If you want to use the identical class for both purposes, but want to keep them instantiated and scoped separately, you would just use different keys for the session. Majority of beans we use are used for RO. For example: displaying a user name, display some content. Those beans do not get mapped. Here is a RO example. public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { MyForDisplayBeanInPageScope b = new MyForDisplayBeanInPageScope ); //b.populate(); //or other code to populate the bean request.setAttribute(myForDisplayBeanInPageScope, b); //return (mapping.findForward(Success)); } //exec The above puts the bean in request scope, thus making it accessible for JSP tags on any JSP page..The author use the decapitalized bean class name as key, such as myForDisplayBeanInPageScope for the class MyForDisplayBeanInPageScope, but any name will do. When using same bean class for both, you may have more than one getter for a property, as we illustrated earlier: one for String getters and setters; The other getter is for the native type, such as long or Date. If values for Struts R/W tags (such as text input fields) need to be localized, this is done in the getter method of the bean.
Copyright ASF
66 A R/O bean is localized in the JSP using JSTL, such as FMT tag, as shown earlier.
Logging
You should never use System.outs They cannot be turned off for production, and even one System.out can cause performance problems, since it is a I/O operation to a device (console), and with 100s of concurrent users, it gets executed a lot. This is how you can use logging:
import org.apache.commons.logging.*; public class SomeClass extends SomeThing { public final static Log log = LogFactory.getLog(SomeClass.class.getName()); public void SomeMethod() { .. log.debug("I am in here and value is" + _someVariable.toString()); .. }
In production we can now centrally turn of all debugging. Any exceptions could be logged at the error level, log.error(e), and they would still be logged to a file and displayed to console.
Copyright ASF
67
Review
You should know how to write a Struts bean, and how to use it for R/W and RO.
Copyright ASF
68
Copyright ASF
69
setValue(Task_Name, arg); } // ... }
Write a getter and setter that matches the forms (jsps) properties. Use a macro (bgs cntrl-space) for get/set. Keep in mind that later, when we work with db, if you have an integer coming from DB, you will not be able to cast it to a String (but will have to receive an Integer, and convert that to String).
Using Windows / Preferences / Java / Editor /Templates, review macros. 7. Optional: Write a String testIt method that returns a getTaskName() in Task Bean. Ex: String testIt() { { return getTaskName(); } Using Test Servlet in Core Bean package, instantiate the Bean, and output the testIt. Ex: TaskBean tb = new TaskBean(); Out.println(tb.testIt()); You can add a few more messages to output.
Copyright ASF
70
Copyright ASF
71
Copyright ASF
72
Copyright ASF
73
Copyright ASF
74 In the Capability Maturity Model (CMM), the SEI defines five capability stages for organizations that develop software. When they did an analysis on a large number of organizations, including professional software development companies, they found that the vast majority were only at the first and lowest stage of the CMM.
Copyright ASF
75
Copyright ASF
76 How do you react if a task slips? In project recovery, the only thing that has worked for the authors is to immediately reassign the task as soon as the first sign appears that a task will be late by the person it was originally assigned to. Only someone with long development experience can know how long something will take to implement. Furthermore, the task owner must participate in deciding the due date. So if he says, I should get that done by March 15, the project manager says OK, lets write down March 19 on the plan. Sincere buy-in is very import.
Copyright ASF
77
You develop an HTML prototype and PDF mock-ups for the entire application which you can demonstrate to the client for approval. Then you add simple tiles, navigation, and CSS style sheets. You then demonstrate the prototype again to obtain approval for the look and feel. At this point, you have not yet written any beans. Now you write beans and get the entire application to display data (no updates). Then you add updates to the application; then inserts. Then you add validation; then security; then further embellishments such as drop downs. People sometimes ask when should the help screens and user documentation be written: in Step one, mock up. If we do not have enough information to write users docs that is how the users needs to use this application, we sure do not have enough information how to implement it. The key to optimal development is that you do the entire application each time in order to complete the iteration. You get a continual feedback from the client on the which ensures that you meet the requirements.
Copyright ASF
78
Coding to Contract
It means that you write code to implement the API or interfaces. In our methodology, we created HTML mockups and prototypes and PDF report mock-ups. Since the properties of a form bean must map to the properties of the HTML form, you can derive the bean properties directly from the HTML mockup/prototype. Coding to contract then just means that you have to get the beans to work. This involves getting the form beans to create, insert, update and delete data (CRUD). Prototype and mock-ups can be considered as the contract or public Application Program Interface (API), since each follows.
Whom to hire?
Sometimes the authors get asked to be involved in the hiring process. Manager can have a hard time selecting a qualified candidate, how to tell? The key question to ask a candidate is: How many years of professional experience do you have with developing applications that went into production/operation? This will tell you who to hire. Some developers have never delivered a web application to production. It is difficult to judge in advance whether it is something they did that prevented it from happening. However, we do know that some people are just able to deliver. This is who we look for. Every successful organization has a few of these individuals on their projects. Interview questions about candidate personality do not help. A person even with the best personality will not add value to the organization if he or she lacks experience and cannot deliver. If you cannot find people to hire that have production delivery experience, then make sure that the people you do hire are cheap and will not be allowed to touch the source code. They might be of some use after a few years of sandbox code maintenance. On projects, you need a lot of people whom you continue to need in operations, after the construction phase has ramped down: database administrator (DBA), Unix admin, Tech Support, Report writer, etc. Once the application has gone into production, if you have do deliver a new version of the application, you may also need developers. Else you mostly need operations people. The operations people also do most of their work weekends and off-hours, so they work in shifts. There is no need to have two DBAs and two Linux administrators during the day when most of the work is done on the weekend. The senior of them do the major work during daytime, such as restore exercises, and the junior of them cover the nights and weekends for normal operations.
Copyright ASF
79
Exit Strategy
Exiting the development and handing off to production can be tricky. There tend to be a lot of little tweaks that people want to slide in as the project evolves. It sometimes takes weeks to exit the development stage, even after the development has been officially completed and the requirements specification been met. Or... the staging and production environment was not ready. The management was betting against you, that you would be late. Once you are done, we will order the hardware. What? To avoid this, ensure maximum detail for the requirements specification and involve the client. Get him or her to approve your iterations and provide you with feedback, and buy in, such as on the look-and-feel iterations. If you try to get these kinds of approvals late in the project you are bound to have difficulties. Make sure that your staging and production areas are ready plenty of time in advance. Sometimes organizations plan for failure, and order the production hardware once the development is completed. That is not the time to issue a purchase order. Not only will you have unnecessary delays, but you will also massively frustrate your developers who have labored very hard to make their deadlines, and now have to wait until they can see their work in production. Yes, there are bugs, but we sometimes see the rate of new bug (vs solved ones) decline and still no production. Exiting development to production can be tricky and can eat up a lot of budget.
Fixed Bid
Clients would do well to do outsource development to a design studio on a fixed bid. Once development is done, they may not need the developers around. In this way, they can outsource a lot of the risk. They can just have admin to operate the system or to write custom reports.
Project Politics
Some good developers are not well educated on office politics, and they should be. One definition of the word politics is incompetence. This helps you translate: Q: Why did you do it this way? A: Because of politics. Translation: Because of incompetence. Some people are successful in organizations purely by playing the politics. They take credit for the work done by competent people and shift the blame for their work to the competent people. This design pattern can be hacked. It is nave not to play politics with the incompetent people. Solution: Do not let people take credit for your implemented designs.
Copyright ASF
80 Also, do not drop your work, to help the politicians. If they claimed that they would do something for someone, let them do it, maybe they can. If they cant get it done, they may blame the competent, but this is a better outcome. Next time they will learn and ask the competent. Dont be nave, you will be taken advantage of, and the project promotions to leads for future projects will go to the wrong people. Then junior developers end up working for the politician. Do not be nave, you hurt the rest of the software engineers.
Review
An experienced software development project manager using a proven methodology process can provide stability to a software project. Or fail it, regardless of how hard developers work. In review labs, you now have gone from a mock up (html) to a Struts prototype. The prototype does not talk to the database. We have created a Struts action that creates a form bean and we display in tiles/JSP, via navigation and we have a look and feel approved.
Preview
This part of the book has covered the development environment set-up, database model design, mock up and prototypes and process, as well as basic MVC with JSP, actions, mappings and beans. In the next part of the book, you will build on this foundation to do advanced MVC, crud, drill down, and more advacanced way of doing beans and actions, as well as some OO.
Copyright ASF
81
.. <html:submit value=Save/> </html:form> Note: taskName property on a form (jsp) needs a getter called getTaskName in formBean. By convention, get/set TaskName must be upper case in bean which is converted to lowercase in properties taskName 4. Edit Struts-config.xml 5. Declare a form bean. Ex: <form-beans > <form-bean name=taskBean type=com.yourOrg.TasksBean /> 7. Modify the taskEdit mapping to use the named (declared) form bean: <action path =/userEditPg type=com.yourOrg.TaskEditAct name=taskBean scope=session validate=false> <forward Remember that when declare a bean in a action mapping, Struts instantiates if for you. 8. Test to see if you are getting values from the bean.
Copyright ASF
Copyright ASF
83
Copyright ASF
84
The difference between a good and a poor architect is that the poor architect succumbs to every temptation and the good one resists it.
Ludwig Wittgenstein
Copyright ASF
85
Copyright ASF
86
Copyright ASF
87 adapt the SQL statements used. If you have properly encapsulated your data access, and not have SELECT statements all over the place in your code, your migration task becomes much easier. Hiding the data access implementation allows you to change the implementation later without having to touch the business logic. For example, if someone forces you to use EJB Entity Beans for data access, and you manage to encapsulate this access using the DAO pattern, you can later replace the Entity Beans with another data access implementation that proves better performancewithout having to rewrite your business logic. If you choose JDO for your implementation, and something better than JDO comes along over time, you can switch it out easilyas long as you have properly wrapped it with a DAO. As mentioned earlier, form beans should not contain database-related code, and should delegate all CRUD work to DAOs. Any bean should be agnostic as to how it obtains its data. Looking at the bean, one should not be able to tell if it received its data from a message queue or an SQL database. This is done by having a separate DAO class that the bean talks to. A bean only has public getters and setters, not much else. It may be worth noting that Suns more recent reference implementation of a web application (Adventure Builder), unlike the earlier Petstore, does not use EJBs but follows the DAO pattern.
Copyright ASF
88
Copyright ASF
89 for optimization. In real life, when O/R layers are chosen, many times the O/R functions wind up being bypassed. We will implement our DAO using iBatis DB layer. Read more about IBatis at www.ibatis.com
You see that this XML file is where the SQL statements go. When the mapped statement UserSelectOne is called, its query is executed and the result returned in the format specified in the result map: a list of java.util.HashMaps, one for each row, with property names id, name_last, name_first. Also note that DAO provides automatic distributed data caching and flushing at the Model layer. SQL statements are thus outside your Java code; this allows you to work on your application when optimizing your SQL. A SQL expert can tune the SQL without having to touch Java. The column has to map the db field name in select, but the property name could match the bean getter, for example FIRST_NAME column maps to FirstName property. is a
Copyright ASF
90
The above example assumes that the container is aware of a J2EE connection pool named bppool and the SQL mapping XML file was stored in the application source code tree at the shown location under the name UserSQL.xml. Your DAO implementation could then read this sql-map-config.xml into a sqlMap object in a static initializer as follows:
static { log = LogFactory.getLog(DAOIBaseHelper.class.getName()); _sqlMap = null; try { java.io.Reader reader = Resources.getResourceAsReader( properties/sql-map-config.xml); _sqlMap = XmlSqlMapBuilder.buildSqlMap(reader); _sqlMap.setCurrentDataSourceName(ids); } catch(Exception e) { log.fatal(XX no iBatis config found + e.toString()); e.printStackTrace(); } }
This assumes that the sql-map-config.xml is accessible in the /properties directory (like resources/properties for internationalization resources). By using static intalizer, we get the handle to the JNDI lookup only once for the entire web application.
Copyright ASF
91
iBatis would identify the mapped statement UserSelectOne in the registered SQL maps, execute the query based on the #value# of myprimarykey passed in, and return the query result according to the specification of the ResultMap (a list of HashMaps). Next we talk about techniques in Java to make you more productive.
Copyright ASF
92
Object-disoriented Programming
Many people understand object orientation only academically. OO programming is especially difficult for people who are trained to think in terms of structural analysis and design, and top-down or bottom-up function decomposition. These approaches focus on differences rather than common things. Function-oriented people see that apples are different from oranges and have to be treated differently. OO people see that both apples and oranges are fruit, grow on trees, can be eaten, have seeds, and so on. They will try to share or reuse the same treatment as much as possible. It takes months of discipline to switch your brain from the old procedural approach to OO. Usually, it also takes serious mentoring. If you are under pressure to deliver, it is by no means easy to implement productive OO. However, once you have been able to switch over your synapses and have completed an OO project, you will notice a tremendous jump in productivity. Chances are that you will be to an order of magnitude (10x) more productive. OO does not mean a change of programming language but a change in the way of using that language. Object-disoriented programmers use the verb(noun) paradigm. For example they write code like the following: Print(pdf); Print(text); Print(doc); Print(gif). The code that does the work on an object is not a part of the object. It follows that the print code has to handle error conditions for various object types. The code can be almost anywhere; it is hard to decide where something should be done, and it is hard to maximize reuse. Object-oriented programmers use the noun.verb() paradigm instead. Their code will look as follows: PDF.print(); DOC.print();. The behavior of an object goes with the object. It is not necessary to know how printing happens; the print work goes on inside that object. We also know where to put the code and where to find it. Just because you code in Java does not mean that you do OO. Nothing in Java will tell you that you are not doing OO. The code will compile and execute with no warning. A bad project is one where they say We did not have time to do it in OO. The truth is that if they had done it in OO, they would have had the time to do it in a relaxed manner.
Copyright ASF
93
Copyright ASF
94
Copyright ASF
95
} //for } catch (Exception e) { log.error(X sql DAOdoUpdate + e.toString()); throw new Exception(e); } //catch }//update
This DAO can support multi-row updates with just a few lines of code. A DAO that extends from this base class would have public wrapper methods that call the base class methods. Finally, your UserDAO would look somewhat similar to the following:
public class UserDAO extends DAOIBaseHelper implements DAO { public void populate(long i) throws Exception { doRetrieve(UserSelectOne, i); } public void update(List list) throws Exception { doUpdate(list, UserUpdate, UserInsert); } }
You could have one DAO per entity or formbean; the result map in its SQL map would have a long list of property mappings (all columns of the table, if necessary), and several mapped statements that share the same result map. Most of the times a DAO does a lot of joins. For example, one mapped statement would only query for the columns that are required for the first screen, another mapped statement would query for all columns if needed as such elsewhere. You can use the same result map without necessarily always filling all its properties with values. Once you have that DAO working, you will query it from an action rather than in the bean constructor. Since querying the database could fail (db could be down etc.), it is better to query from a place where such exceptions can be caught, so querying from an action is better than querying from the bean constructor. You also have more flexibility if your query parameters change between requests. You will reuse the UserDAO from the previous chapter. This time, you need to do a query that returns a list of all users, rather than just one, so you add a method that uses a different mapped statement that you will create. It is very easy to create a unit test for DAOs. DAOs can have a testIt() method that returns the result of the test. The implementation of a testIt() method on a DAO can be as simple as the following: The testIt() method on a DAO:
Copyright ASF
96
public String testIt() throws Exception { populate(); return getResList().toString(); }
The testIt() method on a bean: How do you call this test? You would have a servlet that you run in the browser and would return the test result String as a response. The servlet could look as follows:
public class MyTestServlet extends HttpServlet { protected void service(HttpServletRequest req, HttpServletResponse res) PrintWriter out=null; try { out = res.getWriter(); res.setContentType(text/html); MyDao d = new MyDao(); out.println(d.testIt()); } catch (Exception e) { log.debug(Exception in TestServlet:+e); } }
Review
We should access data and persistence via a DAO interface that we implement. A productive way to implement anything in Java is to apply OO.
Copyright ASF
97
LAB M: DAO
PART 1: Create and register a SQL mapping file 1. Important: Review the iBatis DAO documentation. (In docs or from iBatis web site) 2. Clone a UserSQL.xml , .. as TaskSQL.xml, copying it to com.yourOrg coreBeans package. 3. Inspect TaskSQL.xml. See how it maps different SQL queries to the user table right now. 4. Make sure that there are no references to user in the TaskSQL.xml, (search/replace with task) 5. Edit the User specific SQL to Task specific SQL. Test SQL in pgAdmin first. You need to write these in you XML DAO.: 1. TaskSelectOne 2. TaskSelectAll 3. A DAO column name mapping of the results 4. TaskUpdate 5. TaskInsert 6. Modify the sql-map-config in source properties to add the new DAO mapping. 7. To the list of <sql-map resource...> tags, add the following: <sql-map resource=com/myorg/web/TaskSQL.xml/> 8. Save sql-map-config.xml. Result: Created and registered SQL mapping file with working SQL CRUD.
Copyright ASF
98 PART 2: Create a DAO that uses the SQL map. 1. In src/com/myorg/web, create a new class TaskDAO.java. Make it extend from fx base.util DAOIBaseHelper, and implement fx base DAO interface. (the IDE should import it if you right click) 2. Implement methods as follows: public void populateAll() throws Exception{ doRetrieve(TaskSelectAll); } public void populate(long i) throws Exception { doRetrieve(TaskSelectOne, i); //by primary key } public void update(List list) throws Exception { doUpdate(list, TaskUpdate, TaskInsert); } //String should match DAO names. public String testIt() throws Exception { populateAll(); return _olist.toString(); //public property of parent class, query results } Peak at UserDAO.java to see an example. Note that all the Task variations must exist as a mapped statement in the sqlmap. 3. Save TaskDAO.java. 4. In Eclipse, open the definition DAOIBaseHelper.java, by right clicking on the word (when inside the TaskDAO). See how it implements the methods we use from TaskDAO. Note that instead of extending from DAOIBaseHelper, we could implement the database access in TaskDAO. This is example of code reuse, since method are generic and could be used by any DAO. Ex: doRetrieve(), doUpdate(). 5. Test TaskDAO.java. In com/myorg/test/TestServlet.java, edit the code so it looks like this: d = new com.myorg.task.TaskDAO(); out.prtinln(d.testIt()); Note that the servlet has been registered in Web.XML. 6. Save TestServlet.java. Rebuild the project. Run http://localhost/test You should see a list of results in the browser.
Copyright ASF
99
Copyright ASF
100
public void populateAll() throws Exception //delegate to dao { _dao.populateAll(); _rlist = _dao.getResList(); }//populateAll private String getValue(String a) { return (String) _current.get(a);} public void setValue(String n, String p) { _current.put(n, p); } public String getTaskName() { return (String) getValue(Task_Name); } public void setTask(String arg) { setValue(Task_Name, arg); } public String testIt() { populateAll(); return getTaskName(); } // testIt }
Copyright ASF
101
Copyright ASF
102
J a v
e a n
Remember in Struts, you should not access the data source data directly, but it should utilize a DAO for that. The bean does not query the database until its populate() method is called. Since the goal is to call the populate method from an action, and the action has ready access to the bean, we add a helper method populateAll() to the bean that delegates to the DAO. In the execute(...) method of com.myorg.web.MyUserListAct, you would do the following:
execute( MyUserListBean b = new MyUserListBean(); b.populateAll(); request.setAttribute(myUserListBean, b); return mapping.findForward(Success);
Or
execute( MyUserListBean b = (MyUserListBean) formBean; b.populateAll(); return mapping.findForward(Success);
Copyright ASF
103
This uses the bean in scope from the action(controller). When you call /do/myTaskListAct in the browser, the action will be executed, the bean will be instantiated and added to the session if not already there. In this example, the DAO will be populated with data on bean instantiation. Next, the action will forward to the JSP; now the JSP has the populated bean available and can access it using the c tags. The processed JSP will be returned as a response to the browser.
1
c t o r
t r o
l l e r
J S
P
3
2a
D
2b
J a v
e a n
Copyright ASF
104
Copyright ASF
105
Editing Data
In the previous example, the JSP accessed data from the DAO for HTML display. This illustrated the interaction between all the key elements. The only property the bean had was the DAO, with a getter named getDisplayList() that returned the whole content of the DAO in a List. As you know, a form bean to edit data has one pair of getters/setters for each editable field on the JSP. Then we have to take care of transporting the values of those fields to the proper row of the DAO since the DAO may contain multiple rows of data. There may also be cases where the HTML form is multi-row editable, displaying input fields for several rows of data at the same time, much like an Excel spreadsheet does. This more complex situation is discussed later on.
Reusable FormBean
So far, you have used inheritance on the DAOs to reuse common methods and properties assembled in the DAOIBaseHelper class. The example beans shown have been extended from org.apache.struts.action.ActionForm. In the next step, you will further increase reuse by extending all your beans from a base class that you will write yourself. We will use Struts in an object oriented way. To maintain the basic function of the Struts ActionForm class, your base class will extend from it. Struts already provides some classes that extend from its base class ActionForm, such as ValidatorForm. The ValidatorForm has methods that automate the validation of input fields, both client- and server-side. You may call your base class BaseBean. BaseBean itself will extend from ValidatorForm so that validation functionality is available in all your beans.
Copyright ASF
106
public void setValue(String n, Object p) public boolean getAutoCommit() pbulic BaseBean getIndex(int index) trhows Exception public List getDisplayList() public Map returnMap() thows Exception public org.w3c.dom.Document getXDoc() public void insertNew() throws Exception public Locale getLocale() public BaseBean gotoRow(int index) public void beforeFirst() public Iterator iterator() private class IteratorAdapter implements Iterator public abstract String testIt() throws Exception
You may have noticed that the testIt() method has been made abstract which politely forces you to implement the method on all your individual beans.
Iterator Adapter
Sometimes you will need the bean to implement the Iterator interface. The previously shown JSP that uses the iterate tag takes the whole list of data as one, and iterates over it. We used the capacity of the iterate tag to iterate over a List. The iterator interface provides this facility with the next() method. Inside the implementation of the next() method, you can do callbacks, totals, etc. So you can implement the Iterator interface on the DAO, or on the bean. You can implement the iterator adapter like by just saying this: public abstract class BaseBean extends ValidatorForm implements Iterator {
Review
We can develop a bean using object orientation. It could also have a helper DAO class; and we know importance of unit testing.
Copyright ASF
107
public void setX(String arg) { setValue(Y, arg); } You should not have a setter for PK. If you have an integer value in DB, you can use getValueIS and setValueIS to help with cast/conversion. 8. Create 2 populate methods: public void populateAll() throws Exception {
Copyright ASF
108 taskDAO.populateAll(); setupList(); } public void populate(long l) throws Exception { taskDAO.populate(l); setupList(); } Create a module unit test: Ex: public String testIt() throws Exception { StringBuffer sb = new StringBuffer(); populateAll(); sb.append(TaskName is +getTaskName()); etc return sb.toString(); } // testIt Modify Test Servlet to do b = new TaskBean(); out.println(b.testIt()); Start container and navigate to localhost/test Change the unit test (testIt) to modify and save: populateAll(); setTaskName(Clone the smart army); save(); return null; } // testIt Execute localhost/test Bring up the db admin tool (pgAdmin) Browse the table tasks. Did the data change? Note: If a bean does not CRUD outside of Struts placing it in Struts will not make it work.
Copyright ASF
109
Copyright ASF
110 What would a JSP that will dynamically generate this list have to look like?
<c:forEach var="row" items="${beanNameInJSPscope}"> <tr><td> <c:url value="myUserDetailAct" var ="url"> <c:param name="ID" value="${row.id}"/>c:param name="Dispatch" value="Edit"/> </c:url> or <a href ='myUserDetailAct?ID=<c:out value="${row.id}"/><' > c:out value="${url}"/> </a> </td>
You see how the primary key for each row is dynamically added to the Edit link using the c (Core) JSTL tag. This requires a bean that has a getRow method You create a new action myUserDetailAct that can retrieve and receive the message, by making use of the request parameter id. This action should forward to a detail page. The execute(..) method of this action would look as follows:
public ActionForward execute(){ MyUserDetailBean b = new MyUserDetailBean(); request.setParameter(myUserDetailBean, b); //put in scope String id = request.getParameter(ID); //retrieve parameter b.populate(id); return mapping.findForward(Success); }
When this action finds the ID parameter in the request, it uses it to populate the bean with a populate(parm) method that passes the primary key for querying through the bean to the DAO. There is another way to send a message, using a display tag. Ex:
<display:table width="95%" name="fb" property="displayList"> <display:column property="title" width="50%" title="Title" styleClass="text" href="/do/myUserDetailAct " paramId="ID" paramProperty="id" />
They both end up looking same in the browser, and now you can receive a message in the action.
Copyright ASF
111
DISPLAY TAG
The Display tag library is a most popular and advanced tag library library for data grids. It allows you todo multi-page, sortable data grids. You should review the display tag documentation and examples available on the Web.
This can help you to find out what is in scope for a JSP. Commonly people are not sure what is in the session or request scope of their JSP, or they want to snoop other browser information. Any Servlet book has more information on snooping.
Copyright ASF
112 used to decide what to do. This is what our first version of the execute(...) method of userAdminAct could look like:
String parm = request.getParameter(Dispatch); try { if (parm == null) { //do nothing, just forward return mapping.findForward(Success); } else if (parm.equals(Display)) { b.populateAll(); return mapping.findForward(Success); //forward to List } else if (parm.equals(Edit)) { String id = request.getParameter(ID); b.populate(id); b.rowid=0; return mapping.findForward(Edit); //forward to Edit } else if (parm.equals(Save)) { b.save(); return mapping.findForward(Saved); } }
The JSP could send messages to this action with a URL like the following: /do/userAdminAct?Dispatch=Edit&ID=2 The Save button on the edit page could also use this action with a dispatch parameter. The JSP for the edit page could look as follows:
<%@ taglib uri=struts/html prefix=html %> <%@ taglib uri=jstl/c prefix=c %> ... <html:form action=userAdminAct> <table><tr> <td>ID: </td> <td><c:out value=userAdminBean.id/></td> </tr><tr> <td>Last name: </td> <td><html:text property=name_last size=70/></td> </tr><tr> <td>First initial: </td> <td><html:text property=name_first_init size=20/></td> </tr> </table> <html:submit property=Dispatch value=Save/> </html:form>
Copyright ASF
113 ... Clicking on the submit button will trigger the action user AdminAct with a request parameter named Dispatch and a value of Save. The action will ask the bean to save the data in the HTML form, and forward to the location defined in the forwards section of the action mapping in struts-config.xml.
Drill down
The action mapping for this action could look as follows:
<action path=/userAdminAct type=com.basebeans.basicPortal.cms.UserAdminAct name=userAdminBean scope=session validate=false> <forward name=Success path=/WEB-INF/pgs/memberSec/userList.jsp/> <forward name=Edit path=/WEB-INF/pgs/memberSec/userDetail.jsp /> <forward name=Saved path=/do/userAdminAct redirect = true/> </action>
The Success forward just displays the list page after the action has been executed. The Edit forward just shows the detail page after the bean has been positioned on the current row. The Saved forward goes though the action again to force a requery or refresh of the list before the list page is shown again.
1 5
Controller Actor
Edit JSP
4 3 6
2
3
Copyright ASF
114
A user calls the action, and defaults to a list or rows page. He clicks on a row, and a page is displayed that shows that one row, and he is able to edit the page. On a submit, the data is saved. However, the code of the action class can further be improved upon. You will see in the following section how.
Events
In this section we use events to create simpler dispatching with action parameters. Most developers are used to procedural programming where the programmer can control the flow. You have a green screen menu, with choices from 1-12. In this case, the program can keep track of the user's location in the flow fairly easily. A user might decide to go to different sites and then come back to your site at an entirely different location. To adequately deal with this, the web application developer has to discard procedural programming in favor of event-oriented programming and code more defensively. Events need to be dealt with as soon as they occur. An example of an event is a handling a save dispatch. Common Events Over the course of doing a years of MVC development using Struts, the following events are commonly needed and could be reused, but you may think of more:
public String onDefaultExec(ActEvent ae) throws Exception; public String onDisplayExec(ActEvent ae) throws Exception; public String onEditExec(ActEvent ae) throws Exception; public String onSaveExec(ActEvent ae) throws Exception; public String onZoomExec(ActEvent ae) throws Exception; public String onNewExec(ActEvent ae) throws Exception; public String onInsertExec(ActEvent ae) throws Exception; public String onLogonExec(ActEvent ae) throws Exception; public String onNewSessionExec(ActEvent ae) throws Exception;
With events, you can bring reuse to another level. Since many events such as Save, Display are common to a lot of screens, a base action class can be set up to deal with them. Of course, the event-handling process itself is common to all such actions and is coded in the base action class.
Copyright ASF
115 Events mean that your concrete action class has handlers for actions. Rather than having to deal with the dispatching within the execute(...) method of the concrete action, there would be event-handling methods, such as the following:
public String onSaveExec(ActEvent ae) throws Exception { //do custom handling on save handling here return Saved; }
Using reflection, you can set up the base class to run this method when the action receives a Dispatch=Save parameter.
We will instead only have: public String onSaveExec(ActEvent ae) throws Exception { You note that the method above receives an ActEvent which is also created in the base class and is simply a container for useful parameters, such as the current request, the bean instance and the ActionMapping. The following is a fragment of the ActEvent class:
public class ActEvent extends ContextBase { protected HttpServletRequest _req = null; protected BaseBean _bean = null; protected ActionMapping _mapping = null; public void setReq(HttpServletRequest req) { this._req = req; } public HttpServletRequest getReq() { return _req;
Copyright ASF
116
} ...
The way to access the ActEvent properties is with the usual getters and setters. At the least, the ActEvent will have the same four properties the original bean.execute() method has: request, response, form bean and action mapping. If you need to pass other information, you can just add more properties to the ActEvent class. To do a save on the current bean instance, you would now just write the following: public String onSaveExec(ActEvent ae) throws Exception { ae.getBean().save(); return Saved; } The ContextBase class is a HashMap from Struts Chains. It allows you to put properties in there in a thread safe way.
Copyright ASF
117
catch(Exception e) { procE(B.exec, e, ae); throw new Exception(e.getMessage()); } } protected final String dispatch(ActEvent ae) throws Exception { String parm = ae.getDispatch(); String forward = null; String methodName = on + parm + Exec; try { Class args[] = {ActEvent.class}; Method eventMethod = this.getClass().getMethod(methodName, args); Object objs[] = {ae}; forward = (String) eventMethod.invoke(this, objs); } catch(Exception e) { procE(dispatch , e, ae); throw new Exception(e.getMessage()); } return forward; } ... } The interface command is a future part of Struts that allows for CoR pattern.
Copyright ASF
118
You notice that this is very clean code. You know where events are handled. The dispatching mechanism is hidden in the base class. Also, default code for each of the events is already in the base, so if you just need a plain vanilla onSaveExec, it is already in the base, you do not need to write it.
Copyright ASF
119
Typing mistakes in the forward names (success or Sucess instead of Success) are a common source for errors and debugging work. Therefore, you may find it useful to have String constants for them as shown above. The entire flow is illustrated here:
c t o r
t r o
l l e r
J S
P
3
2a
D
2b
J a v
e a n
Review
We can now the Struts action to make it more reusable and we can easily write a handler for a Save dispatch.
Copyright ASF
120
2. Modify as follows
public ActionForward execute(ActionMapping mapping, ActionForm formBean, HttpServletRequest req, HttpServletResponse resp) throws Exception { String parm = req.getParameter(ID); long l = BUtil.longString(parm);
}//exec
3. In the browser, test the following URLs: http://localhost/do/taskEditAct?ID=1 http://localhost/do/taskEditAct?ID=2 for the result.
Copyright ASF
121
4. Open TaskAct.java 5. Edit like this: public ActionForward execute(ActionMapping mapping, ActionForm formBean, HttpServletRequest req, HttpServletResponse resp) throws Exception { TaskBean tB = new TaskBean(); tB.populateAll(); log.debug(task action called: + tb.getTaskName()); req.setAttribute(myTaskBean,tB); // the name you will use in display tag return (mapping.findForward(Success)); }//exec Examine WEB-INF/portlets/cms/ContentAdminLst.jsp example. Copy the display:table section to TaskLst.jsp. Modify property names in display tag to match TaskBean getters. (do not worry about href tag yet, delete it until next lab) Make sure you name in display matches the name you gave in anction (setAttribute). Navigate to /do/task 10. Test We have completed Struts integration of modules.
Copyright ASF
122
LAB P: Messages
We will now send a URL message from a JSP to the action. Open the TaskLst.jsp Modify it to say:
<display:column property=taskName width=50% title=Task Name styleClass=text href=/do/taskEdit?Dispatch=Edit paramId=ID paramProperty=id />
This assumes you have a property called getId() in bean that works. (unit tested). Test. You should be able to navigate to the task list page from the menu. You should be able to click on one of the names on taskList and zoom the detail, for editing.
Copyright ASF
123
Copyright ASF
124
Remove the execute method in TaskAct(). Add code for: On Display event, forward to list. On Edit, forward to the edit page. On Save, save. Ex: public String onDisplayExec(ActEvent ae) throws Exception { XBean XBean = (XBean) ae.getBean(); xBean.populateAll(); System.out.println(we have list data: + xBean.getName()); //debug ae.getReq().setAttribute(myBeanInScope,xBean); //match display tag name return Success;
}
public String onEditExec(ActEvent ae) throws Exception { XBean xbean = (XBean) ae.getBean(); xbean.populate(ae.getParmID()); // no attribute! And not create! System.out.println(we have zoom data: + xBean.getName()); return Edit;
}
public String onSaveExec(ActEvent ae) throws Exception { xBean bean = (xBean) ae.getBean(); System.out.println(saving); // should work if we unit tested bean xbean.save(); // bean asks its DAO helper to save return Saved;
}
<html:submit property=Dispatch value=Save/> // again, make sure that old action and action mapping are not referenced anywhere, and are deleted. 6. Test a save. Did the data change in DB?
Copyright ASF
125 This would complete the next iteration of a project. We have taken it from a prototype, to a integrated read only application. (In real life, we would have to do entire app., all the screens /reports, as read only 1st, before any fancy work)
Copyright ASF
126
Some use Value Objects (VO) and Business Object Beans (BO).
1
B
2B
s .
e a n
C J S P
3
2D
t r o
l l e r
2A
2C
o r m
e a n
/O D
These are the other approaches: A. Simple VO Going to the JSP, a helper class copies from the VO to the form bean. Going back, the action copies from the form bean to VO with a helper factory. B. Form bean implementing VO Going to the JSP, a BO populates the VO-form bean. Going back, the action passes VO-form bean to the BO. C. Form bean with VO facade Bean exposes a single-row VO with a getter/setter. The VO has getters and setters for properties.
Copyright ASF
127 Going to the JSP, an action passes the VO from BO to form bean. Going back, an action passes the VO from form bean to BO. D. Form bean with DAO helper Going to the JSP, action asks bean to populate from DAO. Going back, action asks bean to save to DAO. This book recommends approach D. It works with JSTL, is easy to unit test, has little code and few objects, which is good for performance (less VM garbage collection). The approaches which use Value Objects are an older way of using Struts; it was the original design created by Struts developers. It was used to be able to keep EJBs outside the model, but had scalability issues. There are variations, but the flow ususally includes these following steps: 1. The controller passes a connection to the DAO (which is usually an O/R implementation). The DAO returns data and the controller passes it to a BO bean. 2. The controller or a controller helper passes the data via a VO to the form bean. You are likely to hit issues with multi-row processing. Since multi-row form beans work with arrays, you are likely to need a second set of beans. These architectures have at least two beans and a VO, which creates a lot of work for the JVM garbage collector. Also, there is a lot of non-controller code in the action. Good MVC does not have model code in the action; the action should not work with data. These architectures are also a lot more complex than the recommended architecture Bean with DAO helper. It is almost always a good idea to follow the keep it simple or KISS approach. Forms are sufficiently complex by themselves so you do not want to make your life more difficult than absolutely necessary. Taking the example of a tax form: the corresponding bean will have getters and setters for a persons name. It may also contain two nested beans, one for a list of incomes and one for a list of expenses. If you were using the VO approach, you would quickly have a very complex and confusing structure at your hands. A quick note on a variation of the VO approach which uses DynaBeans: this architecture does not have a form bean, just a business bean. XML mappings of property names are created within struts-config.xml. This approach is hard to unit test since DynaBeans cannot be used without Struts. If you follow the recommended approach, you develop code so that it is most easily understood by the people who read your code. Readability leads to reuse and reuse leads to higher quality and higher reliability.
Copyright ASF
128
* Forward
A new syntax that is available since Struts 1.2 release it the * forward lke this:
<action path="/*Edit" type="myorg.example.{1}EditAct" name="{1}FormBean" > <forward name="success" path="/WEB-INF/jsp/{1}.jsp"/> </action>
It will look at any requests mapping and match it to the *, so for example /do/customerEdit, will have the {1} replaced by Customer. This does allow for less mapping. You may have to put a use case in a same package as other uses cases, and doing mapping is not that hard.
Insert Event
We can also do insert events in our design from prior chapter by adding the following to the JSP which has so far been for display only: <html:form action=/userAdminAct> <html:submit property=Dispatch value=New/> </html:form> You now have access to the full power of events. You would code your event as follows: public String onNewExec(ActEvent ae) throws Exception { UserBean b = ae.getBean(); b.insertNew(); return BConst.EDITD; } In the code above, you just add a blank row in the bean. The JSP will display a page with the blank row. The action needs to set all default values, such as foreign keys. This insert event adds a row in memory only. Once the user has filled the form, he would use the submit button with Dispatch=Save to save the inserted data to the database. This is the same action used for updating modified data on an existing row of data.
Copyright ASF
129
Exception Handling
Before exceptions, code looked like this:
int x; x = doA(); if (x < 0) { doError1(); } x=doB(); if (x < 0) { doError2(); }
Notice how the returned values are checked to see if doA and doB return negative results. This results in code that is hard to read. Good paths (error-free flows) and bad paths (error checking and handling) are mixed. With the advent of exceptions as a means to deal with errors, code can be made to look like this:
try{ doA(); doB(); }catch (Exception e){ if (e==x){...} }
The good paths are separated from the bad paths, and the code becomes more maintainable. Some people tend to create their own series of Exception objects (MyCompanyException1, MyCompanyException2, etc). The authors believe that the standard exceptions provided by Java are fully sufficient for most purposes since they can be adapted by providing custom messages with exception.setMessage(My Error message). The code will be more readable.
Exception Flow
All the exceptions bubble up to the container, via the action. All DAO methods should log and re-throw exceptions. It is impossible to guarantee network and database connectivity to 100 per cent. DAOs are likely to generate the most of all exceptions, both in development and in production. The DAO should log any
Copyright ASF
130 exception and re throw it to its bean, so that the respective action can find out about it. This is an example for this mechanism in the DAO:
public populate(long i) throws Exception{ try {...} catch (Exception e) log.error(e); throw new Exception(e); } } {
Beans might not need to handle exceptions. Beans delegate work to DAOs and should not do much work with data results other than reformatting and remapping. In the bean, throw the exceptions up to the actions as follows:
public save(long i) throws Exception{ } dao.update(i);
An exception thrown by the DAO is automatically passed on to the action which has called the save(...) method on the bean. Actions receive the exceptions passed on to them by beans. They will handle the exceptions and sometimes throw them to the container so that an error screen will be displayed to the user. The following is an example of an exception thrown by an action to the caller. The caller in this case is BaseAct, which will provide some code to display the error to the user. The action could have code as follows:
public String onDisplayExec(ActEvent ae) throws Exception{ try { UserBean b = new UserBean(); b.populate(ae.getRequest().getParameter(ID)); } return BConst.SUCCESSF; catch (Exception e) { log.error(e); throw new Exception(e);
We also set up web.xml to display the normal errors in a user friendly way.
<error-page> <exception-type>java.sql.SQLException</exception-type>
Copyright ASF
131
<location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/error.jsp</location> </error-page>
Note that the action exception exposes it to the container, so that in production we can check the application servers log to see any issues. These exceptions indicate a hardware, network, or software problem. You must check the log file of production systems regularly to be able to remedy such problems.
t r o
l l e r
t a i n
e r 3
2
D
1
J a v
e a n
Multi-row Update
Struts has built in support for multi row updates, an update where you change more than one row and submit. Lets say you have firstName in row 1 and firstName in row 2, etc. What happens on a submit? The data entry field name_last may be repeated 10 times on a page, and each time the name of the entry field is the same. So how does the server know which row of data maps to which fields? Struts has an indexed property available for this. A JSP that is multi-row updatable might look like this:
Copyright ASF
132
<c:forEach var=row items=${userBean}> <tr> <td> <html:text property=name_last name=row indexed=true size=50/>
For the above to work, the source of multi-row data (taskBean) has to implement Collection. Magically, the rows present in the bean are mapped to the fields, and values transported forth and back. You could use the indexed property in more complex ways, for example referring to a property of the bean which implements Collection, but the easiest to handle is when you implement the Collection interface on the bean itself.
Transactions
The DAO has tBegin() and tCommit() methods to indicate the boundaries of a transaction. Sometimes transactions span several database engines and we need a two-phased commit. The DAO can do it, and we just have to write a custom save method on the bean which should receive the instruction to the complex update from the action. The code in the bean could look as follows: public void save() throws Exception { _dao.tBegin(); this.save(); bean2.save(); bean3.save(); _dao.tCommit(); }
Copyright ASF
133
Master-Detail Processing
One-to-many relationships are omnipresent in database designs. The following figure shows a master-detail relationship between a project and its tasks.
r o
j e c
t t a s k
i d r_ o n j a m . . .
p t a t a d r e s o
i d r _ o i jd s k _ n a m e s k _ t y p e u e _ d a t e u r c e _ r e s p . . .
s i b
l e
Copyright ASF
134
The above code assumes that the project_id was passed to the action as a request parameter as in /myTaskAct?project_id=XX. This may have been generated from a list of projects with a JSP similar to the following:
<%@ taglib uri=struts/logic prefix=logic %> <%@ taglib uri=jstl/c prefix=c %> ... <table> <logic:iterate id=row name=projectBean property=displayList> <tr> <td><c:out value=${row.id}/></td> <td><c:out value=${row.proj_name}/></td> <td> <a href=/do/taskAct?Dispatch=New&project_id=<c:out value=${row.id}/>>Add task</a> </td> </tr> </logic:iterate> </table>
...
Bookmarkable action
One nice feature of the Web: to bookmark or place a Favorite on a specific page of the application and come back to it in one step, without having to go through a series of operations and without having to click oneself through to the requested location. Any solid web application should provide bookmarkable URLs that allow the user to go back to the preferred place, even after weeks have passed, by going to one specific, reliable URL. This means that a maximum amount of the state of the application should be reflected in the URLs. The web application should be able to recover with the information provided in the URL and lead the user to the same screen even after long interruptions (possibly after having logged in again). One way to do this is to go via a tile definition mapping like this:
<definition name="userDetailDef" extends="admDef"> <put name="title" content="User Detail"/> <put name="left" content="../bpadmin/leftPage.jsp"/> <put name="main" content="/do/userDetailAct"/> </definition>
Review
We covered inserting, multirow updates and exeptions.
Copyright ASF
135
Copyright ASF
136
Copyright ASF
137
Copyright ASF
138
... This assumes that the Struts form to be validated (userBean) has the properties getNameLast() and getNameFirst(). With a listing in the depends property, several validation rules can be combined in the order of preference, such as required, minlength.
Copyright ASF
139 If a field is empty, a message to this effect is generated. If it is not empty, but the length is not sufficient, a different message is generated. The key property is combined with the rule names to generate the respective message. For easy localization and customization, message strings are located in the properties file at properties/messages_xx.properties. By default, Struts comes with properties file containing entries as follows:
# Errors errors.footer= errors.header=<h3><font color=red>Validation Error</font></h3>You must correct the following error(s) before proceeding: errors.ioException=I/O exception rendering error messages: {0} errors.required={0} is required. errors.minlength={0} cannot be less than {1} characters. errors.maxlength={0} cannot be greater than {1} characters. errors.invalid={0} is invalid. errors.byte={0} must be an byte. errors.short={0} must be an short. errors.integer={0} must be an integer. errors.long={0} must be an long. errors.float={0} must be an float. errors.double={0} must be an double. errors.date={0} is not a date. errors.range={0} is not in the range {1} through {2}. errors.creditcard={0} is not a valid credit card number. errors.email={0} is an invalid e-mail address.
A message generated by the validation framework would then be The last name is required. or The last name cannot be less than 2 characters. You are free to customize the messages and provide a degree of politeness customary in your culture. To activate Struts validation, and register the above validation.xml, you add an entry to struts-config.xml similar to the following:
<plug-in className=org.apache.struts.validator.ValidatorPlugIn> <set-property property=pathnames value=/WEB-INF/lib/validator-rules.xml, /WEB-INF/config/validation.xml/> </plug-in>
Copyright ASF
140 Several validation files can be added with the comma separator. Default validations provided by Struts (such as the basic rules the custom validation depends on like required, minlength, maxlength, etc.) are found in validator-rules.xml.
Validation Messages
How are validation messages generated and put in scope? Struts provides the methods validate(mapping, request) and saveErrors(request, actionerrors) for this. A typical implementation may look similar to the following action code:
public String onSaveExec(ActEvent ae) throws Exception { ActionMessages errors; BaseBean bean = (BaseBean) ae.getBean(); errors = bean.validate(ae.getMapping(), ae.getReq()); if (errors.isEmpty()) { bean.save(); return "Saved"; } else { this.saveErrors(ae.getReq(), errors); return "Edit"; } // else }
Of course, this implementation should be a part of BaseAct for reuse. The validate(..) method can produce one or more validation errors at the same time (Last name could be too short and First initial could be empty). If any action error is returned, the action forward should bring the user back to the page which produced the error, after putting the errors in scope and thus make them available for display. However, you could also forward to a separate error page.
Copyright ASF
141
The errors tag will display the action errors which the bean method saveErrors(...) put in scope earlier. Default entries in messages.properties provide formatting prefixes and postfixes, such as:
errors.header=<h3><font color=red>Validation Error</font></h3>You must correct the following error(s) before proceeding: errors.prefix=<b> errors.postifx=</b> errors.footer=
If your validation messages themselves contain raw HTML, you may use the html:messages tag for full control, such as in the following:
<html:messages id=error locale=en> <span class=validation><font face=Helvetica> <c:out value=${error} escapeXml=false/> </font></span><br> </html:messages>
Copyright ASF
142 The messages tag can be used to display any messages, not limited to action errors. The implementation shown above iterates over the action errors, which are put in scope under the key id error. Furthermore, you can create your own action errors in addition to those provided by Struts validation. This could be achieved in the execute method of the BaseAct as follows:
catch (Exception e) { ActionMessages errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_ERROR, new ActionMessage(errors.formError, e.getMessage()==null?e.toString():e.getMessage())); saveErrors(request, errors); return getActionMapping().findForward(FormError); }
This would display all error messages on exceptions caught (such as database connectivity problems) on a separate page under the forward FormError. As required for the html:errors tag, errors are stored in request scope under the key error, or ActionErrors.GLOBAL_ERROR . In conjunction with the messages tag shown above, the file messages_en.properties would have to contain an entry like the following:
errors.formError=The following error was encountered when processing this page: <br>{0}
Copyright ASF
143
<td>First initial: </td> <td><html:text property=nameFirst size=20/></td> </tr> </table> <html:submit property=Dispatch value=Save/> </html:form> <html:javascript formName=userAdminBean/> ...
You would add this kind of client-side validation after the server-side validation works.
Business Rules
An example of a complex validation is not to allow an order if a client exceeds the permitted credit limit. You might tag your business rules with an interface such as this:
public interface BusinessRule { public ActionMessages validate (BaseBean b, String rules_name) ;}
By tagging the rules with an interface you know where to find them. It the business rule, typically you create DAOs to check tables, using a Business Rules SQL mappings you create. To use this rule, you should override the Struts validate() method in baseBean/validatorBean. First call super.validate to check any simple declared rules in XML. Then create your business rules objects, and append any Messages to return. As always you can unit test the bean. Then to use rules, check them from your action before a save as in: ActionMessages errors = formBean.validate(); If there are messages, that means you want to put them in scope and forward back to the input page: this.saveErrors(ae.getReq(), errors); You could also do a CoR pattern to call other business rules.
Error page
Any exceptions thrown by action are visible by the container.
<error-page>
<exception-type>java.lang.Exception</exception-type> <location>/error.jsp</location>
Copyright ASF
144 </error-page>
Duplicate submit
During QA or production, sometimes people do multiple submits. This can confuse any application server. Struts has built in handling for submits, using an autogenerated request token. Here is an example of preventing duplicate submits, even when user spawns another browser duplicating the session:
public String onEditExec(ActEvent ae) throws Exception { saveToken(ae.getReq()); BaseBean bean = (BaseBean) ae.getBean(); bean.populate(ae.getParmID()); return "Edit"; } public String onSaveExec(ActEvent ae) throws Exception { ActionMessages errors; if (isTokenValid(ae.getReq())) { class resetToken(ae.getReq()); //method on Struts base saveToken(ae.getReq());//method on Struts base class
BaseBean bean = (BaseBean) ae.getBean(); errors = bean.validate(ae.getMapping(), ae.getReq()); if (errors.isEmpty()) { bean.save(); return "Saved"; } else { this.saveErrors(ae.getReq(), errors); return "Edit"; } // else } // if token else { return onTokenError(ae); //submit the duplicate entry event
Copyright ASF
145
} // on save
Review
We have now reviewed validation.
Copyright ASF
146
Preview
At the end of part two of this book, you have created a solid and extremely efficient application infrastructure, which allows you to benefit from a high degree of reuse. You have the building blocks to create a high performance application. With this infrastructure, you can also build modules rather quickly. The next part will focus on providing richer user interfaces and ensuring that an application will scale well in a production environment, and a look into the future.
Copyright ASF
147
LAB V : Validation
Edit validation XML in WEB-INF/config. (location is set in struts config xml plugin configuration). Note it has validation for a form bean userBean (name must match Struts-config form bean declaration), and a few properties are declared as required,minlength. Run the app. Navigate to join as a user, but leave all the fields blank, and click save. See the error message. This is what we will add to tasks. Add validation for the tasks form bean. Make some fields required and minimum length. Edit TaskEdit.jsp. Add the errors tag to display messages:
<html:errors />
Edit task Act. On Save Exec, call the validate method. Put error message in scope (look at example code in base) Ex:
public String onSaveExec(ActEvent ae) throws Exception { ActionMessages errors; BaseBean bean = (BaseBean) ae.getBean(); errors = bean.validate(ae.getMapping(), ae.getReq()); if (errors.isEmpty()) { bean.save(); // no errors return BConst.SAVEF; } else { this.saveErrors(ae.getReq(), errors); // put it in scope return BConst.VALIDERRORF;
} // if } // saveExec 6. Test server side validation. 7. Add client side code to emit and call JavaScript (like User.jsp in membersSec)
Copyright ASF
Copyright ASF
149
Copyright ASF
150
Copyright ASF
151
Copyright ASF
152
<property name=id column=ID /> </result-map> <mapped-statement name=ContentSrch result-map=result inline-parameters=true> SELECT page_url, title, author FROM CONTENT WHERE title LIKE #stitle# AND CONTENT LIKE #scontent# </mapped-statement> <mapped-statement name=ContentSelectPage result-map=result cache-model=content_cache inline-parameters=true> SELECT page_url, title, author, content, content_type, topic, id FROM CONTENT WHERE page_url = #value# AND content_type = PAGE </mapped-statement>
Note that a content cache is also used here: the content returned by the query is maintained in a cache for ten minutes or until a new item is inserted or an existing item is modified. Further, you verify that the result-map contains all the columns of the SELECT statement of the mapped statement ContentSelectPage. The ContentDAO will use the newly added mapped statement in a method that could look as follows:
public void populate(Object o) throws Exception { } doRetrieve(ContentSelectPage, (String) o);
A c:out tag can emit this content on the JSP, which could look as follows:
<c:out value=${contentBean.content} escapeXml=false/>
Since the content is likely to contain HTML tags, the excapeXml parameter with value of false will avoid an automatic conversion to visible characters (such as the conversion from < to < in order that the browser will display < as in X<Y).
Copyright ASF
153
Content Administration
The screenshots illustrate what a content administration module could look like. A list page would provide a list of all content items available, with links to the detail page for editing.
Copyright ASF
154
The above will display a list of search results, each with a link to the actual content, and the name of the author.
Copyright ASF
155
If the SQL works in the SQL console, you can be sure that it works in the DAO mapping. The quick test in the SQL console will avoid having to debug later. The DAO mapping can then be set up as follows:
<sql-map name=contentSrchSQL> <result-map name=result class=java.util.HashMap> <property name=page_url column=PAGE_URL /> <property name=title column=TITLE /> <property name=author column=AUTHOR /> <property name=content column=CONTENT /> <property name=content_type column=CONTENT_TYPE /> <property name=topic column=TOPIC /> <property name=id column=ID /> </result-map> <mapped-statement name=ContentSrch result-map=result inline-parameters=true> SELECT page_url, title, author FROM CONTENT WHERE title LIKE #stitle# AND CONTENT LIKE #scontent# </mapped-statement>
Note that the mapped statement in the SQL map expects to receive stitle and scontent as parameters. Also, the mapped statement ContentSrch does not return the content itself since the content is not displayed in the result list (only title, author etc. is shown). The result map, however, contains all columns since we want use them in more detailed queries later. It is not required to fill all columns of a result map with every mapped statement.
Copyright ASF
156
ContentDAO will reuse generic query functionality in its base class as follows:
public void populateSrch(Map parm) throws Exception { } doRetrieve(ContentSrch, parm); //method in baseClass
When the contentSrchAct is called, the above code will pass the search parameters to the DAO that will retrieve the items using the mapped statement defined there.
Copyright ASF
157 ContentBean is populated with the list of query results, to be displayed by the result JSP. What is the most important step in the above? Using the testIt() method of the bean!
JavaScript tree
Here is an example of a javascript tree tag:
Frequently, you want to display content hierarchy in a tree that expands in a browser with out a submit. You can also do a server side tree via the Struts menu. The content hierarchy is usually stored in a SQL table via a self join.
Copyright ASF
158 Examples As a part of basic portal there are the following working production examples that you should use when needing a similar need: CMS Ads * Portal Membership management Forums Mail Shopping Cart Credit Card Processing Survey Reporting Graping Security Blog * For example, a great way to server out Ads is as CMS. You just need to display, track and report. This way there are no delays for external sites. These are done using Struts and demonstrating best practices, with the main guiding goal of Simplicity. Because of Struts, these modules are easy to customize and maintain. The big point of basicPortal is to provide 80% of code common to 80% of projects. Since each project is different, students can then develop the last 20% that is custom and that deals with specialized requirements. Anything that is common would be found in bP as an example. Anything that is an exception or specific to a few cases would be up to you. BasicPortal has integrated IDE, Aplication Server, SQL Ending and other things so that you can unzip and hit the ground running. Care has been taken to only utilize accepted best practices.
Copyright ASF
159
And to make it work in HiveMind, you then map the interface, with a file that must be in src/META-INF/hivemodule.xml:
<?xml version="1.0"?> <module id="asyncB" version="1.0.0"> <service-point id="charge" interface="bPe.async1.charge.Charge"> <create-instance class ="bPe.async1.charge.ChargeImpl"/> </service-point> <service-point id="news" interface="bPe.async1.news.NewsSync"> <create-instance class ="bPe.async1.news.NewsSyncImpl"/> </service-point> </module>
You can see how by changing the mapping file, you can change what the implementation will be. You could then utilize the services like this:
public void exec() { NewsSync news = (NewsSync) _reg.getService("asyncB.news",NewsSync.class); Charge commerce = (Charge) _reg.getService("asyncB.charge",Charge.class); commerce.send(); news.receive(); }
Copyright ASF
160
Asynchronous Processing
Any task that could potentially be time consuming should be done asynchronously so as to not to hold up the application server. Also any tasks that that involve talking to another data source, for example talking to RSS, Mail, credit cards services, etc. All these tasks should be done as asynchronous console applications, outside the application server. It is a mistake to try to send Mail or talk to RSS from JSP. Imagine a user joins a site and you want to confirm the e-mail. Above process can run as a cron loop job, checking the database for new users and sending out an e-mail. Even when we write console apps, we can keep using the same beans. We just change the SQL Config.xml like this:
<datasource name = ids factoryclass=com.ibatis.db.sqlmap.datasource.SimpleDataSourceFactory default = true > <property name=JDBC.Driver org.postgresql.Driver /> value=
<property name=JDBC.ConnectionURL value= jdbc:postgresql://basebeans.com:5432/bp2?autoReconnect=true /> <property name=JDBC.Username <property name=JDBC.Password </datasource> value= bpuser /> value= password />
Containers are best at running many quick tasks, such as JSP and servlets that take nanoseconds, not a task that may take a second or more. Maybe even the server is down.
1. WWW 2. RSS
3. Console App.
4. DB
II.WebApp
Copyright ASF
161
Copyright ASF
162
Element billAdrsEl = new Element("billing"); billAdrsEl.addContent(new Element("addrnum").addContent(streetNum)); billAdrsEl.addContent(new Element("zip").addContent(postal)); billAdrsEl.addContent(new Element("name").addContent(fullName)); billAdrsEl.addContent(new Element("address1").addContent(adres1)); billAdrsEl.addContent(new Element("state").addContent(state)); orderEl.addContent(billAdrsEl); Document order = new Document(orderEl); ChargeHelper cHelper = new ChargeHelper(); // here we use a helper class that send the XML via internet Document result = cHelper.charge(order); // here we receve an XML of the response, that is mostly the aproval code } // next }// send()
You can see how we make an XML Document for sending. Shopping card is an important way to build a relationship with your customer.
Copyright ASF
163
helper.reportPrepare(repXML, this); java.util.Map argm = new HashMap(); String repJasper = rep + .jasper; helper.reportFill(repJasper, this, argm); String repOut = helper.reportEmitHTML(req); out.println(repOut); out.println(e.getMessage()); } }
This servlet relies on a ReportHelper class that prepares, fills and returns a Jasper report. Note that we are using a helper, following our OO ideals. Jasper is a very popular open source report-generating package, available at http://www.sourceforge.net/projects/jasperreports. Jasper needs a bit of help to get it to work with iBatis DAO and Servlet engine, which is what helper does. The servlet receives the report name as argument, and optionally, further arguments that the ReportHelper may need. Any component that passes these arguments to the servlet will receive a report.
Copyright ASF
164
In Jasic, the report servlet shown above is registered in web.xml at /rep/handler. This means that a report can be returned to the browser with a URL similar to the following: http://localhost/rep/handler?report=UsrReport1 [TODO: maybe add Screen shot of iReport designer here]
The previous section used a report form in XML format available at /WEBINF/reports/UsrReport1.xml. Report forms for Jasper are standard XML and may be created with any text editor. However, you may consider using iReport, a powerful open source tool for creating report forms. It can set up reports in HTML, PDF, XML and other formats. It is a visual designer allows you to visually insert fields and map them to columns in tables of your database. iReport is available for download at /http://www.sourceforge.net/projects/ireport.
Copyright ASF
165
Copyright ASF
166
Copyright ASF
167
Copyright ASF
168
<role-name>GUEST</role-name> </security-role> <security-role> <role-name>ADMIN</role-name> </security-role> </web-app>
To secure an application that uses Struts actions to access pages, the rights to each page should be set up on the action URLs that are called to access these pages. In the above example, the first security constraint ensures that only logged in users with ADMIN role are permitted to access the administration modules with userAdminAct and contentAdminAct. The second security constraint defines a members-only area of the site, where only logged in users, including guests and administrator can add news or comments. The login-config section specifies that all unauthenticated requests to actions with a security constraint forward to an HTML form, which will typically have to provide the login form to enter username, password and a login submit button. The security roles are also declared in WEB.XML as shown above. Of course, there are still some pieces missing for everything to work, as you will see in the next section. Some developers try to build a home grown security system outside of the J2EE, this is a bad idea when you can extend container-managed authorization.
Copyright ASF
169
connectionName=bpuser connectionPassword=changeme userTable=usr userNameCol=real_email_id userCredCol=passwrd userRoleTable=usr roleNameCol=role_code /> This is the same realm configured in Resin resin.conf: <web-app id= app-dir=c:\jasic\Myagent\myapproot > <authenticator id=com.caucho.server.http.JdbcAuthenticator> <db-pool>bppool</db-pool> <password-query> select passwrd from usr where real_email_id=? </password-query> <role-query> select role_code from usr where real_email_id=? </role-query> </authenticator> ... </web-app>
When moving from testing to a production environment, be aware that the content of your user repository and user rights may need to be modified. You do not want a user with admin rights left over from the test environment mistakenly purge the production database after several days of operation. Further, the application server and database server should not be run as the UNIX root user. The database account used by the web container should not be an administrative account and should not have write privileges if not needed. Generally speaking, production servers should be secured by an experienced administrator using a hardening script such as Bastille. All servers should be protected from potentially disgruntled employees with physical security and user account policies.
Membership
Most applications have to develop membership management. You want to store the member information in the database, allow users to add themselves and have an admin screen where you can assign a role.
Copyright ASF
170 You could also have a cross reference table that would link many to many as to what content they like. Or even FOAF (Fried of a Friend) cross reference many to many table that indicates who are the friends of the member are. This give you a way to drill down to FOAF content.
Copyright ASF
171 This test statement should retrieve all rows that meet our search criteria AND do not have a security setting. As a second step, use your SQL console to add some values to entries in the usr table, as in: usr: id, real_email_id, passwrd, organization, role_code 1, salesrep1@basebeans.com, 111, SALES_US, ENHANCED 2, salesrep2@external.com, 222, SALES_US, STANDARD 3, salesrep3@infonoia.com, 333, SALES_EU, ENHANCED 4, salesrep4@external.com, 444, SALES_EU, STANDARD As a third step, use your SQL console to add some values to entries in your contact table, as in:
name_last, name_first_init, roleViewID, roleModID, orgID Cekvenich, V., STANDARD, ENHANCED, SALES_US Gehner, W., STANDARD, ENHANCED, SALES_EU
Consequently, for the user salesrep2 the following SQL should be generated to view the items:
SELECT name_last, name_first_init, tel FROM contact C, usr U WHERE U.real_email_id=salesrep2@external.com AND C.orgId = U.organization AND (C.roleViewID IS NULL OR C.roleViewId IN (U.role_code))
The SQL map that is used to display a list of all items that match the security criteria would have a mapped statement as follows:
<mapped-statement name=ContactSecureList result-map=result inline-parameters=true> SELECT name_last, name_first_init, tel FROM contact C, usr U WHERE U.real_email_id=#value# AND C.orgId = U.organization AND (C.roleViewID IS NULL OR C.roleViewId IN (U.role_code)) </mapped-statement>
Copyright ASF
172 The content bean would pass the key for the logged in user to the mapped statement as follows:
public void selectSecure(Object key){ doRetrieve(ContactSecureList, (String) key); }
The action that will show the list of contacts could have a method, as follows:
public String onDisplayExec(ActEvent ae) throws Exception{ ContentBean b = (ContentBean) ae.getBean(); Object userKey = ae.getRequest().getSession().getAttribute(user_key); b.selectSecure(userKey); return Success; }
This assumes that the lookup key for the user (here: real_email_id) was stored in the session during the login process.
Copyright ASF
173
SELECT name_last, name_first_init, tel FROM contact C, usr U WHERE U.real_email_id=#value# AND C.orgId = U.organization AND (C.roleViewID IS NULL OR C.roleViewId IN (#role_string#)) </mapped-statement>
This assumes that the bean passes both user key and role_string to the mapped statement.
Copyright ASF
174
SSL
We should be able to encrypt our content as it goes over the wire, so that we can get to the web site like this https://localhost We can use the keytool that comes with JDK
Keytool genkey alias Tomcat keyalg RSA
Then we have to setup the keystoreFile in the server, which is specific to every container. We would go to https mode anytime we issued a password or a credit card number. (One of the example modules is a shoping cart). Built into basicPortal is support for Struts SSLExt, that makes it easier to switch back and fort from http to htts.
Geocode User IP
Sometimes there are denial of service attacks that come from obscure places. We can protect our web application by writing a filter that will ignore all requests that come from blocked regions. One can acquire a database that codes what IP ranges comes from what location. We can find the IP of the user going to the site, like this:
String ip = request.getRemoteAddr();
Based on this information, we can write a simple filter, that intercepts the requests, and does a query to find out the geo coded location, to see if you are allowed to get to this site, from your location. Sometimes you also want to capture the users IP, in case you want to block them from your site for any reason.
Copyright ASF
175
Other Security
Windows has a reputation as being hard to secure. With Linux, there are hardening packages such as Bastile. It is important not to run your container at root level. Also, in SQL, the creator of the tables should not be the same user that you connect at as a web app db user. So we have a SQL admin that creates a SQL table, and they grant some privileges to bpuser. Mostly the router (CISCO et al) is exposed to outside internet, and application servers are in DMZ, while the DB server is behind the fire wall.
Review
Web is full of opportunity for mischief, but we can do some diligence and harden our web site.
Copyright ASF
176
LAB AB : Security
1. With the application running, click join to add a user. 2. Modify web.xml to require a user to be a GUEST role to view tasks. Examine the urls at end of web.xml for syntax. You can just go to the guest security constraint. In there add a url pattern for /do/tasksPg/* 3. Restart web server, navigate to localhost/ (home page) and test to see if you can get to that page via menu, or another way. 4. Select * from usr to find the password, or add your own record to usr table.
Copyright ASF
177
Based on this requirement, how would you implement a formBean that matches it? You end up nesting beans within each other, and using a dot notation to access properties. Within one form bean you can have is a/has a implementation like so:
public class CompoundBean extends BeanA { private BeanB _b = null; public BeanB getBeanB() { return _b; } }
Copyright ASF
178
Nested Beans
Consider a tax form as shown above. It has address information, a list of income and a list of expenses. One person has many incomes and expenses. Data for income and expense is likely to be stored in a different database table, in a many-to-one relationship to person data. The number of income/expense items will vary, and the form should allow editing of the lists. At the JSP layer, you would uses Tiles to combine sub-JSPs in a master-JSP, in a similar fashion as shown above. But what about the form bean? In this module, you really have three form beans, each drawing data from their own DAO. The beans are fairly independent. For example, when using the page, you could add many expenses without ever updating data on the person. This is a situation where you may consider nesting beans. How would you construct such a complex page? Each sub-JSP has its own bean. First, you would unit test each sub-JSP with its bean. Then you would create a bean for the master-JSP on which you carry instances of the sub-beans as properties. The compond-bean could nest beans as follows:
public class TaxBean extends BaseBean{ protected PersonBean personBean = null; protected IncomeBean incomeBean = null; protected ExpenseBean expenseBean = null; public TaxBean() { personBean = new PersonBean(); incomeBean = new IncomeBean(); ExpenseBean = new ExpenseBean(); } public PersonBean getPersonBean() //returns instance of bean return personBean; } public IncomeBean getIncomeBean() //returns instance of bean return incomeBean; } public void save(); super.save() incomeBean.save(); expenseBean.save(); } public String testIt() { { {
Copyright ASF
179
super.populate() incomeBean.populate (); expenseBean. populate (); super.setPropX() incomeBean.setPropY(); expenseBean.setProX(); super.save() incomeBean.save(); expenseBean.save(); return null; // the db should be updated, and bean maps the form making it easy. }
Note that in the compound bean, we have to override save() to ask support to save if any, and then any nested beans. The concept of nested beans following complex forms is an important one. Do you see how we can now unit test CRUD for the compound bean, before we try to integrate? Cool. Rather than having the TaxBean just returning String properties, you return a bean or a collection. In an action, you could then access the properties of PersonBean with something like myTaxBean.getPersonBean().getNameLast();
Dot Notation
Properties of nested beans can be read from the JSP with what is called dot notation. This is the same mechanism we used in part 2 of the book in the iterate tag which used a property=dao.resList. For example, a non-nested property would be addressed with the following: <c:out value=${myBeanName.myPropName}/> A nested property could be addressed, as follows: <c:out value=${myBeanName.myNestedBeanName.myPropName}/> In the example above, we could write, as follows: <c:out value=${taxBean.personBean.name_last}/> This assumes that:
Copyright ASF
180 (a) the action has put TaxBean in scope using the key taxBean, (b) TaxBean has a public property getPersonBean() which returns PersonBean, and (c) PersonBean has a public property getName_last() Many tag libraries, such as JSTL, support nested properties.
Nested Tag
While JSTL tags support nested properties, the Struts html tag library tags for input fields do not support these nested properties. This creates a potential issue where your form bean is nested inside a master bean and/or you have several form beans. A Struts extension tag library nested: exists where you could write as follows:
<html:form action=/mySub1Act/> <nested:nest property=mySub1Bean > <nested:text property=mySub1Prop/> <nested:submit property=Dispatch value=Save/> </nested:nest> </html:form> <html:form action=/mySub2Act/> <nested:nest property=mySub2Bean > <nested:text property=mySub2Prop/> <nested:submit property=Dispatch value=Save/> </nested:nest> </html:form>
This assumes that the master bean has been put in scope and has the methods getMySub1Bean() and getMySub2Bean() which return the instances of the respective beans. Does using the nested tags create an issue if you want to use a JSP that is not nested? That is where mySub2Bean is put in scope rather than the master bean? You could maintain the nested syntax if you add a method to mySub1Bean as follows: public class MySub1Bean extends BaseBean { ... public MySub1Bean getMySub1Bean() //getter to return itself { return this; } } This would ensure that the nested tag can operate on the bean itself rather than looking for a nested bean. The way you know you have mastered Struts is if you can do above nested module from scratch, in less than half a day, or a few of these type modules per day without strain.
Copyright ASF
181
Review
You should know how to create compound beans that contain nested beans and how to unit test crud on the compound beans.
Copyright ASF
182
Copyright ASF
183 .etc. Make sure you carry the FK at all times. 5. Unit test the nested (compound/complex) bean. Note: Most forms are nested (compound/complex). Since FormBean needs to map to the From, we often need to create nested (compound/complex) beans.
Copyright ASF
184
Copyright ASF
185 This would obtain the collection of available roles from the form bean in scope. The form bean could look as follows:
public class UserBean extends BaseBean { protected String role = null; //inner class, but would usually be in a separate class class OptionBean //this encapsulates one option label and value { private String label = null; private String value = null; public OptionBean(String label, String value) this.label = label; this.value = value; } } ... public void setRole(String role) this.role = role; } public String getRole() return role; } public Collection getRoleOptions() { List options = new ArrayList(); //implements collection options.add(new OptionBean(Sales, 101)); options.add(new OptionBean(Marketing, 202)); options.add(new OptionBean(Accounting, 303)); return options; } { { {
} See how that there is get and set for the current property? And a getCollection to give you a set of choices? This matches the tat select property and options collection.
Copyright ASF
186
By default, the optionsCollection tag tries to obtain the display value by accessing the label property of the option bean, and the storage value by accessing the value property of the option bean. Struts will store the selected value in the role property of the form bean, as specified in the surrounding html:select tag. Before you move to obtaining the values from the database rather than hard coding them on the bean as above, you may wish to add a frequently required feature: a null value at the top of the list. This could look as follows:
<html:select property=role> <html:option label=---Please select one--- value=-1/> <html:optionsCollection property=roleOptions/> </html:select>
If the user does not select one of the items in the options collection, the role property of the bean will have the value -1. In the Save action, you could validate the role property and throw an error message if the value is outside the valid range.
Note how the result map creates an instance of OptionBean for each row returned in the result list, rather than a HashMap as previously shown. If you already have a roleSQL.xml with other mapped statements and result maps, you could just add this mapped statement and result map to it.
Copyright ASF
187
Copyright ASF
188
public class OptionsSelDAO extends DAOIBaseHelper { populateByType(Object sType) } ... } The DAO would be accessed from a helper bean as follows: public class OptionsSelBean { public Collection getOptions(String type){ myOptionsSelDAO.populateByType(string); return myOptionsSelDAO.getRowList(); } { doRetrieve(OptionsByType, (String) sType);
The bean that should serve the roleOptions list of option beans would have a getter method as follows:
public Collection getRoleOptions() { if (_options == null) _options = new OptionsSelBean(); return _options.getOptions("ROLE"); }
Options choices are cached at the model layer in Struts. Also note that options collection never joins in SQL to the decoding table, thus the SQL join executes faster, as we will learn later. Benefits of Options Select are: Less validation Less typing Higher performance (no SQL join, browser decodes) Data driven Site (adding choices in DB, and the flow of the application changes)
Review
Copyright ASF
189 With this chapter, you have created an infrastructure for creating efficient access to data for drop downs.
Copyright ASF
190
Unit test the UserBean getTaskStatusOptions() in test Servlet See if the JSP lets you update status of task via a drop down. After testing, add a new choice via db, and see if it shows up. Optional: Review the options sql.xml.
Copyright ASF
191
It is simple (built like HTTP servlets that do request-response in XML). You can do browser-side XML-RPC and activate an XML RPC implementation in your project like this: <script type="text/javascript" src="./scripts/init.js"></script> Server-side, once you have working formbeans, you can expose them to XML-RPC.
Copyright ASF
192
To make it real XML RPC we can use an xml.apache.org package like this: XmlRpcServer xmlrpc = new XmlRpcServer (); xmlrpc.addHandler ("examples", new ExampleHandler ()); ... byte[] result = xmlrpc.execute (request.getInputStream ()); response.setContentType ("text/xml"); response.setContentLength (result.length()); OutputStream out = response.getOutputStream(); out.write (result); out.flush (); You can access this from a JavaScript in a browser
function xmlhttp() { xh = new XMLHttpfRequest(); xh.open("POST", "test.txt", true); xh.onreadystatechange = function() { if (xh.readSate==4) { alert(xh.responseText()) }} xh.send(null);
Copyright ASF
193
xh.setRequestHeader("MessageType", "CALL"); xml.send("some html"); }
It allows you to send and receive messages to servlets without a submit. You should start the advanced stuff by using an xml-rpc JavaScript library. Establishing a xml rpc in javascript can be as simple as: var server = new xmlrpc.ServerProxy(serverURL, methods); or service = XMLRPC.getService("xml rpcs service url"); depending on what .js library you use. More on XML RPC home page.
Copyright ASF
194 for a more comfortable user experience. An added benefit is that processing is offloaded from the server to the user PC. With Axis, any Struts application can be made to provide SOAP web services very quickly. I now tend to look at light weigh WS such as XML-RPC over SOAP.
Flex
An interesting tag library from MacroMedia Flash is Flex, for example:
<%@ taglib uri="FlexTagLib" prefix="mm" %> <html> <% // In a real-life app, you get the XML data by invoking a method o n a JavaBean String data="<employee name='John Smith' email='jsmith@mail.com '/><employee name='Jane Doe' email='jdoe@mail.com'/>"; %> This is html<br> <mm:mxml> <mx:Application width="300" height="200" xmlns:mx="http://www.m acromedia.com/2003/mxml"> <mx:Model id="myModel"> <%= data %> </mx:Model> <mx:DataGrid widthFlex="1" heightFlex="1" dataProvider="{my Model.employee}"/> </mx:Application> </mm:mxml> </html>
This renders a Flash grid on a users browser, and anyone that does JSP can use it.
Copyright ASF
195
Flash
Flash component in the browser can provide a rich, data-bound user interface that draws its data from a server in XML format. Going much beyond the splash screens everyone has become accustomed to, Flash can provide data handling features that are quickly nearing those of heavy client applications (such as javascript client applications). By executing the application on the client, you are likely to achieve performance improvements, a better user experience, and higher developer productivity when using existing server-side application infrastructure with remote access through web services.
It follows the open SWF (pronounced swiff) file format specification used by action script. Flash graphics are vector based and look decent on displays with different resolutions. A Flash application reacts almost instantaneously after the files have been downloaded.
Copyright ASF
196
The SWF specification is publicly available. You can find information about SWF at http://www.openswf.org. SWF files can run on Linux, MacOS, Windows, PocketPC and some Palm and phone devices. By coding to flash, we do not need to worry about cross-browser issues.
Copyright ASF
197
Creating Components
You can subclass and create your own visual components that developers can place on stage in Actionscript 2.0: Import mx.core.UIObject;
Class myPackage.MyCommonent extends UIObject { static var symbolName:String = MyComponent; static var symbolOwner:Object = Object(myPackage.MyComponent); # include ../core/ComponentVersion.as function MyComponent() {} function init(Void):Void { super.init(); } function size(Void):Voide { suprt.size(); } }
Copyright ASF
198
Blob upload
Images and such are just another form of content. As such it is best managed for large sites inside of a database. One would have a Servlet that would allow us to emit this content in the appropriate section of a JSP, and a way to upload a blob to the db. Commons-upload should help you upload. To display, something like this:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { File f = new File(System.getProperty("user.home") +"\\zoewrap.jpg"); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new FileInputStream(f)); BufferedImage image =decoder.decodeAsBufferedImage() ; // Send back image ServletOutputStream sos = response.getOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(sos); encoder.encode(image); } and <img src="/servlet/ImageServlet?id=n&rnd=some_random_number"/>
Software as Utility
Distributed processing has evolved in waves, oscillating back and forth between client and server. The 60s and 70s had host-based processing on 3270s and VT100 terminals. The 80s brought processing to the client, with Client-Server, such as PowerBuilder running on a Windows PC and Sybase or Oracle Database running on the Server. The 90s brought back host-based processing, with HTML emitted by application servers and
Copyright ASF
199 minimal processing on the browser. Software is now being partitioned back to the client server model. Application servers can provide WS, and scripts execute in a browser. The big jump in productivity is that sometimes we do not have even to provide a application server to provide the Web Service for our browser scripts. We can just use 3rd party Web Services provided, such as those provided by Google, Amazon and Ebay. Since we do not need to write that part, the cost of development goes down. ToDO: JDNC
Review
MVC applications allows us to separate the presentation layer and the data layer. We can use this Client/Server approach to develop web services or to consume the services and XML from within our browser. By writing scripts, especially actionscript, we can have rich UI. Flash brings in a lot of useful visual components that we can leverage for development, with no run time costs. Above all, make good looking applications.
Copyright ASF
200
Copyright ASF
201
Copyright ASF
202
return mapping.findForward(forward); }
This will generate report data that may look similar to the following:
[TODO: JAMon report screen shot here]
Loading Data
Stress testing reduces costs because it allows you to finding problems earlier is much cheaper than finding problems after the application has gone to production. When testing, the database should have more than just a few rows of data, since that would not test much, everything would be in the db cache. How many records should you have in your db tables? Well, if you plan to have 30 million records in your databases after 5 years of operation, that is how many records you should have. DBMonster can be used to generate sample records and insert them into your database as row. The databases memory cache needs to be exhausted in these tests. A DBMonster configuration file dbmonster-config.xml may look similar to the following:
<!DOCTYPE dbmonster-schema PUBLIC -//kernelpanic.pl//DBMonster Database Schema DTD 1.0//EN http://dbmonster.kernelpanic.pl/dtd/dbmonster-schema-1.0.dtd> <!-- Schema generated by SchemaGrabber --> <dbmonster-schema> <table name=content rows=1000000> <key databaseDefault=true> <generator type=pl.kernelpanic.dbmonster.generator.MaxKeyGenerator> <property name=columnName value=id/> </generator> </key> <column name=title type=varchar nulls=0> <generator type=pl.kernelpanic.dbmonster.generator.StringGenerator> <property name=minLength value=5/> <property name=maxLength value=70/> <property name=allowSpaces value=true/>
Copyright ASF
203
</generator> </column> <column name=content_htm type=varchar nulls=0> <generator type=pl.kernelpanic.dbmonster.generator.StringGenerator> <property name=minLength value=80/> <property name=maxLength value=900/> <property name=allowSpaces value=true/> </generator> </column>
The program is compiled and executed with a command similar to the following:
java -cp .;dbmonster.jar;pg74jdbc3.jar;commons-cli.jar; log4j.jar;commons-beanutils.jar;commons-logging.jar; commons-collections.jar;commons-digester.jar; pl.kernelpanic.dbmonster.DBMonster -c dbmonster-config.xml -s dbmonster-schema.xml
Copyright ASF
204
Stress Testing
p.
pS
e r v
e r
When testing the application server it important to consider the network IO. For example NewISys 2100 has dual gigabit NICs. Most PC and Laptops ship with a single gigabit card, but almost all server have dual. The first thing that gets saturated on the stress test is sometimes the NIC. Using a regular 100 megabit card on an application server does not make sense. It will be the first thing to saturate. Also, to do a stress test, you need several workstations to generate traffic against the application server. A useful tool to conduct module load testing is OpenSta. Refer to the OpenSta lab in this book to find out more about OpenSta.
Copyright ASF
205
Database Server
A database server should have a maximum amount of memory and a maximum IO including SCSI R/W Cache possible. The more RAM the better, the more R/W cached I/O channels the better. You can review NewISys 2100 as a SCSI R/W cache machine and support for a lot of memory in a 1U size, configured to about $4,000US. To configure db for memory, for example a Linux server with 8 gigabytes of RAM (), one should allocate all but 100 megabytes to the database, assuming the application server is located on another machine. The remaining 100 megabytes are used by the operating system. The file /data/postgresql.conf on a Linux machine with 8 gigabytes should include a parameter configured similar to the following: shared-buffers = 987500 In PostgreSQL, each buffer is 8K in size. By default, PostgreSQL is configured to use only 512K of RAM. In Linux, the kernel.shmmax in sysctl.conf should also be increased as follows: kernel.shmax = 7900000 This will tell the operating system to allow a process to use more RAM.
Application Server
Database servers often have the maximum memory possible with current technology. On the other hand, application servers usually do not need more than two gigabytes of RAM. Application servers should be configured, like database servers, to make use of all available memory except for 100 megabytes. As an example, Resin would be started on a two gigabyte UNIX machine, using the Java flag as follows: httpd -J-Xmx1900m You obtain a help list of available Java flag by typing java -X in a console. A system can have a lot of application servers, each with a DAO cache. However, there is usually only one database server, because replication across multiple database machines slows everything down, unless manual vertical or horizontal partitioning is used. If resources are constrained, always allocate more resources to the database server ( and pay attention to I/O channels and R/W cache). In production with multiple application servers, one can configure most DAOs, including iBatis to flush all the caches for critical tasks.
Copyright ASF
206
Co-Location
A good idea is to store your servers at an unmanaged co-location facility. How to you calculate the required bandwidth? T1 allows for about 5.250 KB/s. Therefore, if you commonly use pages with images are 40 kB, that means that you can serve out about 130 pages per second.
JDBC DataSource
The DataSource pool has to work in sync. with garbage collection. Let say that you did not configure parallel garbage collection for your JVM properly. On a large system, a garbage collection could take 20 seconds. During that time, a DataSource would time out in the middle of a retrieve, causing intermittent exceptions. A timeout setting for a datasource pool should be over a minute, just in case, and you should configure the JVM to garbagecollect in parallel.
Copyright ASF
207
You can address efficiency issues by analyzing the paths selected by slow queries.
Copyright ASF
208 decide an index path would reach the desired data, or whether a complete data scan would be most efficient. Database engines are usually very fast when executing SELECT statements. Index pointers are constantly updated and optimized. If a row is inserted or updated, tree branches may need to be split up and reorganized.
The illustration below shows a 3-way table join. For every desired row found in Table A, rows are searched in Table B. For every desired row found in Table B, rows are searched in Table C. Consider the situation where there are 10 qualifying hits in table A, 100 in table B and 1.000 in Table C. When done in a single query, this means that more than 100.000 records have to be searched via binary trees or data scans. As the number of joined tables increases, the number of inner table iterations increases in a nonlinear fashion. This is why table joins usually are the biggest computational cost factor in a data driven application. It may not be necessary to redesign the database schema when optimizing queries. Often, you can help the SQL engine pick a better path by reducing the number of joins in a query, for example by splitting a query. The following is a query across six table joins:
Copyright ASF
209 select a.col1, f.col7 from a, b, c, d, e, f where a.col2 = b.col2 and b.col3 = c.col3 and c.col4 = d.col4 and d.col5 = e.col5 and e.col6 = f.col6 This could be rewritten, as follows, to reduce the number of searches required: select a.col1, c.col4 into #temp1 where a.col2 = b.col2 and b.col3 = c.col3 select #temp.col1, f.col7 from #temp1, d, e, f where #temp1.col4 = d.col4 and d.col5 = e.col5 and e.col6 = f.col6 The best way to achieve a high performance is to create a good E/R design in the first place. DBAs should be the most experienced staff on the project. However, for them to be able to help create a solid database design, you need to provide them with very detailed mock-ups. Of course, having followed the development approach advocated in this book, you have these mock ups readily available. Professional database design cannot be learn in one weekend; experience is paramount. Still, the authors recommend SQL Puzzles and Answers by Joe Celko in order to learn about useful techniques, with good examples.
Stored Procedures
Experienced database developers implement stored procedures to help lessen the complexity of the first few steps in query execution. Stored procedures can use intermediate tables and forced path and they can hide complex operations from Java developers. An SQL expert can apply his or her skills here to optimize the query path. Stored procedures can call other stored procedures. Sometimes, stored procedures are hundreds of pages long.
Copyright ASF
210
Copyright ASF
211
Fail over
Here is an example of a typical high load environment.
S t ic k y B it
es
si
e r v e r
a c h e la y e r
p t i o R aO l F a i l n a n dD S SD B
v e r
e q u e s t s S o f t w a r e o r H a r d w a r e
e r v e r
B M a in D B
e r v Ce r
Once you find a load of a single box , you can add boxes to get your desired scalability. You can see that there is only one DB, which is why SQL design is so important. Typically you have a CISCO switch up front to set a sticky bit for a users session. The sessions get assigned to the server in a round and robin scenario. Once a server gets a user session, all future requests go to that box. Any changes to the DAO get a distributed cache flush using oscache settings. If a box fails, a user log into a new box and the form beans were saved to db as of a last event.
Review
Monitoring and stress testing the application is an important construction phase step.
Copyright ASF
212
OpenSTA 1. 2. 3. 4. Install OpenSTA and re-start your machine when prompted. Start the OpenSTA Nameserver from the start menu. Start the OpenSTA Commander from the start menu. Once inside the commander, right click on the Scripts folder and choose New Script, then Http 5. Give the script a name, then double click on it to open the Script Modeler tool. 6. Choose Record from the Capture menu. OpenSTA will open up a web browser window and record the pages you browse to use for testing later. Browse to the http://localhost/do/tasks page, then view and/or edit several tasks. Once complete, click back on the Script Modeler tool and choose Stop from the Capture menu. Save this script. 7. Now that a test script has been created, right click on the Tests folder and choose New Test, then Tests. 8. After giving the test a name, double click on the test name to edit the test. 9. Click on the script that you created above, and drag it into the Task 1 field of the test. This tells the test which script to run, a test can run multiple scripts. You can also set options for the task down below, such as how many times each user should run the task, etc.. 10. Under the VUs column, enter the number of virtual users you want to be simulated in the test. Once you have the parameters setup the way you would like them, click on the green arrow in the tool bar to run the test. 11. Once the test has completed, the results will be listed under the results tab in a folder for the date/time the test was run. Open the folder, and select the checkboxs for the metrics you would like to view.
Copyright ASF
213
role_code region_code city source_code passwrd text text, text, text, text,
topic_interest_code text,
role_code, region_code,
Copyright ASF
214
Copyright ASF
215
While you may have your production environment hosted off-site at a co-location facility in order to save $ on bandwidth, you should never outsource the roles of DBA operations and Security.
Change Management
If your application is successful, new modules will be added frequently. And you will be asked to create many more applications. How do you manage the versions of the JARs that you use in your different applications in /WEB-INF/lib? Image that you want to use Struts 1.3 jars, but you have a few applications in production that are Struts 1.1? Should you go back and regression test? Most of the time, the cost of configuration management is higher than the cost of regression test. That is keeping track of what application has what jars is high. It is cheaper to upgrade all the applications to the new platform. Each time you deploy, you are likely to use the latest version of the dozens of JARs in the application. If you have 5 applications with 12 JARs each, innumerable different combinations of JARs are possible, and you quickly have an organizational nightmare. It is likely that you will be better off updating the existing applications to the latest JARs. Maven (see next section) can help with automating some of this chaos.
Copyright ASF
216
CVS
For team development, Concurrent Versions System (CVS) is a popular source control software. Eclipse works well with CVS. The CVS plugin to Eclipse is included with the Jasic suite, but you will need access to an existing CVS repository to use it. Most Linux distributions come with CVS installed. After installation, this is how you unitize CVS. First make a place where cvs file will live such as:
mkdir /cvshome
Then create the user group on Linux (using GUI tools) called cvsgroup. Create an account on your linux box for all the developers and then add them to the cvsgroup. Using the GUI (or command line) make sure that the group can read and write in the /cvshome folder. Then initialize CVS like this:
cvs d /cvshome init
Then you can just create a blank file (using Vim or pico) in your home folder or a temp folder, and chdir to that folder that has one empty text file. You can create a project now via (v/r are optional):
cvs d /cvshome import nameOfTheProject v r
You are now ready to start using it. First you have to add CVS to your IDE like this:
Then you need to check out your project and start adding file to it.
Copyright ASF
217
When you want to check back in, you just right click on the code and click teamsynchronize with repository. Commit will send your files to the CVS and update will get new changes from other team members to your project. Its a good idea to CVS sync at least a few times a day. About once every 10 days, a project lead might tag the files in cvs with a version number, for example year_week, so that people can go back in time. When creating a war file, one should note what year_week tag the release it is.
Copyright ASF
218 Figure 20-1: Servlet engines in production You note that the most popular platforms for production applications in April 2003 were Tomcat (25%), Resin (24.5%) and IBM (24.5%), followed by Oracle/Orion. You should make sure that your application can work on multiple containers. Of course, different application servers have different bugs and provide different degrees of leniency with regard to the code you write. Web apps will grow even more in popularity, since they are so cheap to deploy an so cheap to manage. Imagine the client server days of going around upgrading the VB/ Delphi app on each users desk
Chains Filter
One popular thing to do is install a gZip filter for your application. This reduces page size that is sent via the internet, allowing you so sever more pages per second on the same line. A Chain is a method called in one class that move up a hierarchy to find an objects that execute the method. For example a user request, wrapped in a context. Each object that could handle the context would have an interface to handle the context and it could call the next object if it needs to pass it down the chain. This can be done using a filter for example. A filter implements the filter with a doFilter( signature. A future version of Struts, code named Jericho (in CVS already) will replace the request processor with a chain. It will have a ContextBase object based on HashMap so that you can add messages will be passed down the chain. A chain will extend ChainBase, and be registered with a CatalogBase. Returning false allows the chain to continue. The action signature for Struts 2.0 might look like this:
public boolean execute(Context context) throws Exception {
Copyright ASF
219
Design
Design is best explained as 80/20. You want to only design for the rule, not all the exceptions. This makes it an elegant system that can be maintained. Things that happen rarely are not part of design, but they are implemented as an exception. A new engineer might try to design a complex system, or one that handles all possible exceptions. KISS. What you want to do is spend effort to see what you can remove.
What is Overrated
Certain technologies and methodologies for web application development have proved to be more controversial than others. Extreme Programming: Working without detailed specifications approved by the client is bound to fail. It seems to be a non scientific and a controversial approach UML : UML uses a process-oriented approach. Web application processes should be short and data-driven. UML may be good for documentation after construction. E/R diagrams are great. Test cases, however, are very useful. O/R: At the end, the clients want data displayed in tabular format, so E/R is just fine and easier to optimize with relational databases. EJB: Are relatively complex and are less scalable than other approaches, and should not ever be used for persistence. Visit EJBs 101 Damnations which provides 101 reasons why not to use EJBs (see: http://www.softwarereality.com/programming/ejb/index.jsp). JSF (Java Server Faces): Heavy UI processing on the server side, with potential scalability issues. Use Client side UI. JSF appears to do a lot of complex work on the server. It limits what graphic artists can contribute. Some even say that JSF is a design by committee, or a solution for a probem we do not have. BluePrints: MVC is so much more powerful. It happens again and again that people without commercial production experience advocate a technology. You may wish to ensure that you take advice from people who have actually deployed applications with the tools and approaches they recommend.
Common Mistakes
Here are some common mistakes that might save you time: Not having useful requirements Not declaring the JSP tag in JSP (JSP will ignore the tag)
Copyright ASF
220 Using useBean or bean define (create bean in action instead) Mapping a bean in action mapping and then doing bean b = new bean() as opposed to bean b = (bean) formbean. Using properties in action. Action is a singleton. Using JDBC. You should use a DAO. DB access it the most problematic part of J2EE
And the parting word is: Always code to the least amount of astonishment * (From a book practical programmer)
Copyright ASF
221
Copyright ASF
222
Copyright ASF
223
Appendix A
JASIC - an open-source integrated development suite for web applications basicPortal CMS - a Struts-based portal application with content management ABOUT JASIC It is not trivial to build an application that is viable in the long-term. Developers can avoid costly and time-consuming mistakes if they invest thinking and resources into its architecture, rather than just build and fix it tactically. Jasic is an integrated development suite for Java that leverages several of The Apache Foundation's Jakarta Struts projects into a comprehensive set of features and functionalities for web application development. It provides enormous productivity gains and allows developers to concentrate their resources on features that are unique to their project. Jasic includes the following third-party software for web application development: Eclipse 3 IDE with SolarEclipse web applications plug-in Tomcat 5 and Resin 3 Application Server Fast JRockit 8.1 Java Virtual Machine (VM) from BEA Systems (or use JDK 1.4 from IBM or Sun) PostgreSQL 7.4 database pgAdmin II database tool (Windows only) iReport visual report designer (Windows only) However, the main software component of Jasic is basicPortal CMS (bP), a dynamic, Struts-based portal application with content management that combines the functionalities and features that are required for about 80% of all web projects. basicPortal is not limited to use with the above components. It works out-of-the-box with the components above, but experienced developers may use it with any other J2EE server, IDE or SQL database of their choice.
Copyright ASF
224 Developer benefits: Rapid installation of several open-source components that are pre-configured to work together. Greatly enhanced developer productivity (experienced coders should be able to produce at least two or three modules per day). Extremely solid and stable architecture (community's code contributions increase stability). Ease of use, yet suitable for large-scale data-driven web applications. Breaks complex applications into simple, consistent sets of components. Ensures all developers in the team develop code using the same approach. Encodes best practices as many users help debug and improve open source components. Large developer community provides virtually instant support. Business benefits: Speedy and cost-effective development. Substantially reduced time-to-market and quicker product releases. Open source: access to source code at no charge. Freedom from closed, proprietary systems, thus no licenses to keep track of, renew and upgrade. No dependence on specialist labour and vendors. Numerous features meet a variety of business needs. Highly platform-independent. Greatly modular.
ABOUT BASICPORTAL CMS basicPortal integrates the Jakarta Struts framework with an application server, IDE and connection pools to SQL. While it is easy to use, basicPortal helps to build large-scale, database-driven web applications that perform extremely well. basicPortal includes support for ecommerce/credit cards, news, lead tracking, content syndication, fora, calendars, web logs ("blogs"), wikis, email support, high-speed standard J2EE security, row-based security, images, blobs and uploads.
Copyright ASF
225 basicPortal uses best practices in Java software design, such as J2EE container managed security, Model-View-Controller architecture (MVC 2), JavaBeans, JavaServer Pages (JSP), data access objects (DAO), the JSP Standard Tag Library (JSTL), caching and more. It demonstrates accepted best practices in web Graphical User Interface (GUI) development using standards such as Cascading Style Sheet (CSS), Struts Tiles and Layout. Key features: Runs on top of any J2EE application server, including Websphere, Weblogic, Oracle IAS, Borland, Novell exteNd, Tomcat, Resin and Orion. Is packaged with iBatis SQL database layer for object-relational mapping///SQL DB servers (PostgreSQL, MySQL, Oracle, etc.), but can store dynamic content in any database. Supports multi-row display tag, multi CRUD and user events. Combines best features of Expresso and Scaffolding. Working and configured Jasper Reports. Content management system contains sample HTML and XML data to build on. Flash Action Script forms for rich client user interface. DAO interface and database Layer implementation. Contains member sign up and administration facilities (user login, roles, etc.). Master Detail processing. Uses standard J2EE data source and JDBC Realms for user security. Supports RIA+SOA. THE JASIC DISTRIBUTION The Jasic distribution is an integrated development suite that includes the following programs: basicPortal Eclipse 3 IDE with SolarEclipse web applications plug-in Tomcat 5 and Resin 3 Application Server Fast JRockit 8.1 Java Virtual Machine (VM) from BEA Systems (or use JDK 1.4 from IBM or Sun) PostgreSQL 7.4 database pgAdmin II database tool (Windows only)
Copyright ASF
226 iReport visual report designer (Windows only) Variations on this platform are possible. The Java Development Kits (JDKs) from Sun and IBM can be used instead of JRockit, as long as the JDK 1.4+ compatible versions are used. basicPortal has been tested with Resin, Tomcat, and Orion, but other J2EE application servers compatible with JDK 1.4 and JSP 1.2/2.0 can also be used. baseBeans recommends the use of Eclipse with the SolarEclipse plug-in as an IDE, however others such as IntelliJ can be used as well. PostgreSQL is the recommended database, but any SQL DB would do. A database administration interface such as pgAdmin II for PostgreSQL is recommended. SQuirreL is another good tool that works with various databases such as PostgreSQL, Oracle, and MySQL. Developers are encouraged to make platform choices that best suit their needs and the culture of their organizations. However, it should be kept in mind that variations on the platform will require extra configuration to ensure all pieces work together. Also, some development procedures may vary from those specified in other basicPortal documentation if customizations are made. The latest version of the Jasic distribution for Windows 2000/NT/XP can be downloaded in one ZIP Archive from SourceForge (http://sourceforge.net/projects/basicportal). If you are reading this document from a CD, the CD should include the ZIP Archive. Linux works great and is the ideal platfrom. At this time, users of Linux and other Unixtype systems must CVS source, download, install, and configure all components separately. For all the platforms, developers will find the latest downloads on the baseBeans.com and Infonoia.com downloads page (http://www.basebeans.com and http://www.infonoia.com)
Copyright ASF
227
BASIC INSTALLATION Unzip the Jasic installation zip file into C:\jasic\ or D:\jasic\. To minimize the possibility of conflicts, make sure that there is only one Java Development Kit on your server. This can be done in the Control Panel -> Add/Remove Programs interface and by searching for files such as javac.exe outside of the Jasic directory. The Java Virtual Machine that comes bundled with Windows should not cause a problem. Next, put the Java directories in your system variables. If JRockit will be your JDK, go into the Control Panel -> System -> Advanced -> Environment Variables section and make the following changes:
JAVA_HOME should be C:\jasic\jrockit81 PATH should be %JAVA_HOME%\bin;%SystemRoot %\system32;%SystemRoot% Note: A frequent error in configuration is not setting the one system environment path to %JAVA_HOME%\bin;%SystemRoot%\system32;%SystemRoot %.
If you installed Jasic on a drive other than C:, replace C:\ with the proper drive designation for your system. Bring up a command prompt by running the program command.com (in Start -> Run). Issue the command:
C:\>java -version java version "1.4.1" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1) BEA WebLogic JRockit(R) Virtual Machine (Native Threads, Generational Concurrent Garbage Collector)
If the outcome of the command is consistent with the JDK you have selected for development, you can proceed to the next steps. For any component installation question, you can refer to cheat sheet on baseBeans.com Cheat Sheet page. It is recommended that you download and install a Netscape (www.netscape.com) or Mozilla (www.mozilla.org) browser. Some tags that work in Internet Explorer may not work in other browsers, so it is best to test on a variety of platforms.
Copyright ASF
228
INSTALLING AND CONFIGURING THE DATABASE You must have some SQL engine to create the tables on since data is stored in a SQL DB. To install the bundled PostgreSQL software, first run the PostgreSQL Windows installer in \jasic\pgSQL. Then run the pgAdmin II installer in the same directory. Accept most defaults in the process, other than the installation directories, which can be set to \jasic\PostgreSQL and \jasic\pgAdmin2. Once the database programs are installed, make sure the PostgreSQL service is started in Control Panel -> Administrative Tools -> Services. Then run pgAdmin II from the Windows Start menu. Click the icon in the upper left corner to connect to the database. Click OK at the next dialog box to connect as the default superuser named postgres; you may connect with no password from the local computer. Under Users in the left panel object tree, right-click on postgres and select Change Properties. Give this user a new password for increased security. Next, select (right-click) localhost -> Create Object -> User. Create a new user called bpuser. Give this user a secure password, and grant the user the ability to create new databases. Take note of the user's expiration date and set it far into the future if desired. Exit pgAdmin II and reconnect as bpuser. Select (right-click) localhost -> Create Object -> Database. Create a database called basicportal (no data entry other than the database name is required in the dialog box). It is a good idea to use all lower case names in PostgreSQL. You will now have to run some prewritten SQL scripts to create the tables for use with basicPortal and some sample table data. These scripts are located in \jasic\bp\WEBINF\doco. Under Databases in the left panel object tree, select (right-click) basicportal -> SQL. Select Load, navigate to the file \jasic\bp\WEB-INF\doco\table_create.sql, then select Open and Execute. The basicPortal tables should then be created. Repeat this step for the file \jasic\bp\WEB-INF\doco\table_sample_data.sql. Table data can be viewed and edited by right-clicking on the table name and selecting View Data. If you will be writing additional modules for basicPortal, you should familiarize yourself with these tables. The table usr contains user information and passwords, and is used to implement logins and security. The table content is used to enable content management, and contains web page data. For information on how to manipulate these tables by hand, please see the documentation for PostgreSQL (www.postgresql.org) and pgAdmin II (pgadmin.postgresql.org). Note: You can save yourself time by testing a JDBC connection via something like http://squirrel-sql.sourceforge.net
Copyright ASF
229
STARTING THE APPLICATION SERVER After database installation, the system is ready to be activated. All other Jasic components are preconfigured for easy use. If you installed Jasic on a drive other than C:, edit the file \jasic\resin3\conf\resin.conf. Change the drive letter in the following line: <web-app id='/' app-dir='c:\jasic\bp'> Also, change the db connection settings. <init-param url="jdbc:postgresql://localhost:5432/basicportal? autoReconnect=true"/> <init-param user="bpuser"/> <init-param password="changeme"/> Same is true of other app. servers. There is a sample for Tomcat and Resin Next, start Resin by running the file \jasic\resin-3\bin\httpd.exe. A terminal window will appear, and Resin should report that it is started and listening. In your web browser, navigate to http://127.0.0.1/test. This will test the database connection, and should display the contents of one user object and one content object. Then navigate to http://127.0.0.1. A sample basicPortal web site should appear. basicPortal is now installed and running! You need to customize the content and user rights. If you are configuring your own container, make sure JDBC driver is in commons/lib. Ex: JDBC Realm setting
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99" driverName="org.postgresql.Driver" connectionURL="jdbc:postgresql://localhost:5432/ basicportal?autoReconnect=true" connectionName="bpuser" connectionPassword="changeme" userTable="usr" userNameCol="real_email_id" userCredCol="passwrd" userRoleTable="usr" roleNameCol="role_code" />
Copyright ASF
230
Ex: App. Context setting <Context path="" docBase="c:/jasic/bp" debug="0" reloadable="true" crossContext="true"> <Resource name="bppool" auth="Container" type="javax.sql.DataSource"/> <ResourceParams name="bppool"> <parameter> <name>password</name> <value>changeme</value> </parameter> <parameter> <name>url</name> <value>jdbc:postgresql://localhost:5432/basicportal? autoReconnect=true</value> </parameter> <parameter> <name>driverClassName</name> <value>org.postgresql.Driver</value> </parameter> <parameter> <name>username</name> <value>bpuser</value> </parameter> <parameter> <name>factory</name>
Copyright ASF
231
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value> </parameter> <parameter> <name>maxIdle</name> <value>1</value> </parameter> <parameter> <name>maxActive</name> <value>3</value> </parameter> <parameter> <name>maxWait</name> <value>5000</value> </parameter> <parameter> <name>removeAbandoned</name> <value>true</value> </parameter> <parameter> <name>removeAbandonedTimeout</name> <value>20</value> </parameter> </ResourceParams> </Context>
Copyright ASF
232
</Host>
In the case of Tomcat, you will also need to copy the dbcp and commons-jar to common/lib, so that the connection pool will work in Tomcat. PREVIEW OF BASICPORTAL USER MODULE basicPortal is an example of the code needed on 80% of all web projects, implemented using the best practices and design available. It has sample beans, Struts Actions, and DAOs that can be used and extended by developers. It also contains sample XML files with settings for Struts Menus, Validators, and Tiles so that these files do not have to be written from scratch. basicPortal is currently running on http://www.basebeans.com and http://www.infonoia.com . Users can register to become members of the site, and administrators can edit their information and privileges. PREVIEW OF THE BASICPORTAL CONTENT MANAGEMENT MODULE basicPortal has a content management system (CMS), allowing administrators to add text or HTML content through an easy web interface. All pages are stored in the system database. Each web page has an identifying tag such as HOME or ABOUT. Any page can be displayed easily by passing basicPortal an HTTP parameter such as content=ABOUT.
public class BaseBean extends ValidatorForm implements Collection { public final static Log log = LogFactory.getLog(org.apache.scaffoldingLib.base.BaseBean.class.getNa me()); protected Dao _dao = null; protected List _rList = null; // results protected Map _current = null; // property public BaseBean() { _rList = new List(); } public String testIt() throws Exception { throw new Exception("no test implemented"); }
Copyright ASF
233
public void find (Object parm) { _dao = myDao(); _dao.populate(Long parm); _rList = dao.getList() } public String getX () { return(String) List.get("x"); } public void setX(String arg) { List.put(x,arg); }
Copyright ASF
234 CODE SAMPLE Above is a code sample illustrative of the kinds of techniques used in basicPortal. It is a DAO helper class which contains a status log and a list of results retrieved from its Data Access Object. The object methods allow unit testing to be implemented, and allow the list to be viewed and modified. SUPPORT Infonoia, Europe - www.infonoia.com Infonoia - a play on information and paranoia - builds professional Java/J2EE applications for the Internet, Intranet and Extranet and provides Java/J2EE and Struts training, mentoring, development, products and support (Infonoias team speaks English, French and German). Infonoia is a key contributor to basicPortal and provides commercial support for JASIC and basicPortal CMS in Europe. Infonoia has extensive experience in application server technologies and open source standards. Indeed, it has been the first company in Switzerland and among the first in Europe to use application server technology back in 1998. Its architects and programmers mastered Java web applications long (in IT terms) before it became a standard with J2EE. Infonoias clients include public and private organisations, such as Deloitte & Touche, Tetra Pak, Lombard Odier Darier Hentsch or the Canton and Republic of Geneva. Feel free to contact jasic@infonoia.com with any questions about JASIC, basicPortal or other training, mentoring, development and support services and products. Infonoia SA Rue de Berne 7 1201 Geneva Switzerland Phone: +41 22 900 00 09 Fax: +41 22 900 00 18 Email: jasic@infonoia.com www.infonoia.com baseBeans Engineering, United States - www.basebeans.com baseBeans Engineering is the author of Jasic and basicPortal CMS and provides commercial support for both applications in the USA. The company provides best practices web development services and has worked for NASA, CSC, Ford Motor Company, Bank of Amercia and others. Accomplishments include working with large
Copyright ASF
235 teams, budgets of over one million dollars, and terabyte size databases with over 40,000 concurrent users. Vic Cekvenich is a principal developer at baseBeans Engineering. He specializes in project recovery, has written a book on using the MVC/Struts framework with Data Access Objects (DAO) 14 months ahead of others and was named Trainer of the Year by the Java Developer's Journal.
Copyright ASF
236
Appendix B Tomcat
Copyright ASF
237
Copyright ASF
238 (you have to use the # number as server told you earlier). It will them prompt for session password, which is the password you just setup on the server side. voila! you have the X on MS Windows. random notes: 1) you can click the top left icon on the VNC viewer for a list of options. 2) you can close or disconnect your VNC viewer, and restart and reconnect to the VNC server to see your old session. The old session will be kept on server until you kill the vncserver. 3) on the server, you can stop the vncserver by running vncserver -kill :# (where # is your X display number) 4) the connection between vnc viewer to vnc server is not encrypted, just like normal ftp/telnet. so you should only use it on a local trusted LAN. you should consider using ssh tunneling if you want to use VNC over the untrusted Internet. check this URL for details: http://www.uk.research.att.com/vnc/sshvnc.html
Copyright ASF
239
Initialization
JDateTime can be initialized in many ways. Default initialization sets the current date and time. Further, it may be set to a specific date and/or time. Number of milliseconds since 1970 (e.g. System.currentTimeMillis) may also be used. Date and time of JDateTime object can also be set by each field independently, if there is a need for that. And at the end, JDateTime can be initialized or set from instance of any available Java date/time class, as well as from String, as it will be shown later. JDateTime jdt = new JDateTime(); // current date/time jdt.set(1999, 03, 21, 19, 50, 17.321); // specific date/time jdt.set(2001, 07, 13); // specific date jdt.setYear(2003); // sets just specific year jdt.set(System.currentTimeMillis()); // milliseconds
But JDateTime can also act as a factory for above classes: it can return a new instance of any of above classes. There is also a possibility to store date/time values to an existing object of above classes.
Copyright ASF
240
jdt = new JDateTime(CalendarInstance); // from calendar jdt.loadFrom(TimestampInstance); // date/time from calendar Calendar c = (Calendar) jdt.getInstance(Calendar.class); // new calendar instance Calendar c = jdt.getCalendarInstance(); // shortcut for previous jdt.storeTo(c); // store to existing calendar Besides above default Java date/time classes, it is possible to add so-called 'converters' for any other custom date/time class.
Strings
One of the power features of JDateTime is a possibility to set time from and to get time as a String. There is a default date/time format, but it is also possible to specify custom format, both globally or just for one method call. JdtFormater implementations may be used for custom date/time string setting and getting. Default formatter uses enhanced set of patterns defined by ISCO 8601 standard. jdt.get(); jdt.get("YYYY.MM.DD"); jdt.set("01-01.1975", "DD-MM.YYYY"); // get date/time in default format // get date in specified format // set date from specified format
Copyright ASF