diff --git a/2490.pdf b/2490.pdf new file mode 100644 index 0000000..139099b Binary files /dev/null and b/2490.pdf differ diff --git a/2491.pdf b/2491.pdf new file mode 100644 index 0000000..94c9b92 Binary files /dev/null and b/2491.pdf differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d81faab --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2006 Matthew Moodie + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2993ab --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Pro Apache Ant*](http://www.apress.com/9781590595596) by Matthew Moodie (Apress, 2006). + +![Cover image](9781590595596.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/ch03/build.dtd.xml b/ch03/build.dtd.xml new file mode 100644 index 0000000..952d9b5 --- /dev/null +++ b/ch03/build.dtd.xml @@ -0,0 +1,14 @@ + + + + + Apache Ant book example project. The main targets are listed below. + + + + + + + diff --git a/ch03/build.properties b/ch03/build.properties new file mode 100644 index 0000000..a59da05 --- /dev/null +++ b/ch03/build.properties @@ -0,0 +1,2 @@ +property.example=Local File +property.file.example=build.properties diff --git a/ch03/build.properties.default b/ch03/build.properties.default new file mode 100644 index 0000000..e5c348f --- /dev/null +++ b/ch03/build.properties.default @@ -0,0 +1,2 @@ +property.example=Default File +property.file.example=build.properties.default diff --git a/ch03/build.start.properties b/ch03/build.start.properties new file mode 100644 index 0000000..ab8629e --- /dev/null +++ b/ch03/build.start.properties @@ -0,0 +1,17 @@ +server.name=localhost +server.port=8080 +server.scheme=http +server.manager.name=manager + +server.url=${server.scheme}://${server.name}:${server.port}/${server.manager.name}/ + +j2ee.home=C:/j2ee +catalina.home=C:/jakarta-tomcat + +j2ee.jar=${j2ee.home}/lib/j2ee.jar +jsp.jar=${catalina.home}/common/lib/jsp-api.jar +servlet.jar=${catalina.home}/common/lib/servlet-api.jar +mysql.jar=${catalina.home}/common/lib/mysql.jar + +build.classpath=${mysql.jar};${j2ee.jar};${jsp.jar};${servlet.jar} + diff --git a/ch03/build.xml b/ch03/build.xml new file mode 100644 index 0000000..a69ec38 --- /dev/null +++ b/ch03/build.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The value of property.file.example is: ${property.file.example} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch04/build.xml b/ch04/build.xml new file mode 100644 index 0000000..a1b4fcd --- /dev/null +++ b/ch04/build.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch05/build.properties b/ch05/build.properties new file mode 100644 index 0000000..9c34af4 --- /dev/null +++ b/ch05/build.properties @@ -0,0 +1,65 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties \ No newline at end of file diff --git a/ch05/build.xml b/ch05/build.xml new file mode 100644 index 0000000..81a3156 --- /dev/null +++ b/ch05/build.xml @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch05/src/shared/conf/SQL/plants.sql b/ch05/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch05/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch05/src/shared/conf/SQL/plantsDelete.sql b/ch05/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch05/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch05/src/shared/conf/SQL/plantsDrop.sql b/ch05/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch05/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch05/src/shared/conf/SQL/plantsInsert.sql b/ch05/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch05/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch05/src/shared/conf/database.properties b/ch05/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch05/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch05/src/shared/docs/LICENSE b/ch05/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch05/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch05/src/shared/docs/README b/ch05/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch05/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch05/src/shared/docs/index.html b/ch05/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch05/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch05/src/shared/java/org/mwrm/plants/Constants.java b/ch05/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch05/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch05/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch05/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch05/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch05/src/shared/java/org/mwrm/plants/SelectData.java b/ch05/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch05/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + * + */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch05/src/shared/java/org/mwrm/plants/package.html b/ch05/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch05/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch05/src/stand-alone/docs/index.html b/ch05/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch05/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch05/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch05/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch05/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ * + */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch05/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch05/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch05/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch05/src/web/conf/antBook.xml b/ch05/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch05/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch05/src/web/conf/plant.tld b/ch05/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch05/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch05/src/web/conf/web.xml b/ch05/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch05/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch05/src/web/docs/index.html b/ch05/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch05/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch05/src/web/images/banner_under.jpg b/ch05/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch05/src/web/images/banner_under.jpg differ diff --git a/ch05/src/web/images/cyc_cou.jpg b/ch05/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch05/src/web/images/cyc_cou.jpg differ diff --git a/ch05/src/web/images/gal_niv.jpg b/ch05/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch05/src/web/images/gal_niv.jpg differ diff --git a/ch05/src/web/images/no_image.jpg b/ch05/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch05/src/web/images/no_image.jpg differ diff --git a/ch05/src/web/images/ran_fic.jpg b/ch05/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch05/src/web/images/ran_fic.jpg differ diff --git a/ch05/src/web/images/tul_tur.jpg b/ch05/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch05/src/web/images/tul_tur.jpg differ diff --git a/ch05/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch05/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch05/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + * + * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + * + * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch05/src/web/java/org/mwrm/plants/servlets/package.html b/ch05/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch05/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch05/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch05/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch05/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch05/src/web/java/org/mwrm/plants/tags/package.html b/ch05/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch05/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch05/src/web/pages/footer.html b/ch05/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch05/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch05/src/web/pages/header.jsp b/ch05/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch05/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch05/src/web/pages/index.jsp b/ch05/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch05/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch05/src/web/pages/indexBody.jsp b/ch05/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch05/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch05/src/web/pages/menu.jsp b/ch05/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch05/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch05/src/web/pages/plantStyle.css b/ch05/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch05/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch05/src/web/pages/plants/displayPlant.jsp b/ch05/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch05/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch05/src/web/pages/plants/displayPlantBody.jsp b/ch05/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch05/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch05/src/web/pages/plants/displayResults.jsp b/ch05/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch05/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch05/src/web/pages/plants/displayResultsBody.jsp b/ch05/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch05/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch05/src/web/pages/plants/index.jsp b/ch05/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch05/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch05/src/web/pages/plants/searchForm.jsp b/ch05/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch05/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch05/src/web/pages/template.jsp b/ch05/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch05/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch05/src/web/tags/simple.tag b/ch05/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch05/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch06/build.properties b/ch06/build.properties new file mode 100644 index 0000000..aec73a5 --- /dev/null +++ b/ch06/build.properties @@ -0,0 +1,100 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager diff --git a/ch06/build.xml b/ch06/build.xml new file mode 100644 index 0000000..f8e5341 --- /dev/null +++ b/ch06/build.xml @@ -0,0 +1,899 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${mail.message.docs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch06/src/shared/conf/SQL/plants.sql b/ch06/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch06/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch06/src/shared/conf/SQL/plantsDelete.sql b/ch06/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch06/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch06/src/shared/conf/SQL/plantsDrop.sql b/ch06/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch06/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch06/src/shared/conf/SQL/plantsInsert.sql b/ch06/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch06/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch06/src/shared/conf/database.properties b/ch06/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch06/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch06/src/shared/docs/LICENSE b/ch06/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch06/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch06/src/shared/docs/README b/ch06/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch06/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch06/src/shared/docs/index.html b/ch06/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch06/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch06/src/shared/java/org/mwrm/plants/Constants.java b/ch06/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch06/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch06/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch06/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch06/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch06/src/shared/java/org/mwrm/plants/SelectData.java b/ch06/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch06/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch06/src/shared/java/org/mwrm/plants/package.html b/ch06/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch06/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch06/src/stand-alone/docs/index.html b/ch06/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch06/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch06/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch06/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch06/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch06/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch06/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch06/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch06/src/web/conf/antBook.xml b/ch06/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch06/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch06/src/web/conf/plant.tld b/ch06/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch06/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch06/src/web/conf/web.xml b/ch06/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch06/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch06/src/web/docs/index.html b/ch06/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch06/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch06/src/web/images/banner_under.jpg b/ch06/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch06/src/web/images/banner_under.jpg differ diff --git a/ch06/src/web/images/cyc_cou.jpg b/ch06/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch06/src/web/images/cyc_cou.jpg differ diff --git a/ch06/src/web/images/gal_niv.jpg b/ch06/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch06/src/web/images/gal_niv.jpg differ diff --git a/ch06/src/web/images/no_image.jpg b/ch06/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch06/src/web/images/no_image.jpg differ diff --git a/ch06/src/web/images/ran_fic.jpg b/ch06/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch06/src/web/images/ran_fic.jpg differ diff --git a/ch06/src/web/images/tul_tur.jpg b/ch06/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch06/src/web/images/tul_tur.jpg differ diff --git a/ch06/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch06/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch06/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch06/src/web/java/org/mwrm/plants/servlets/package.html b/ch06/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch06/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch06/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch06/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch06/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch06/src/web/java/org/mwrm/plants/tags/package.html b/ch06/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch06/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch06/src/web/pages/footer.html b/ch06/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch06/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch06/src/web/pages/header.jsp b/ch06/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch06/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch06/src/web/pages/index.jsp b/ch06/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch06/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch06/src/web/pages/indexBody.jsp b/ch06/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch06/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch06/src/web/pages/menu.jsp b/ch06/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch06/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch06/src/web/pages/plantStyle.css b/ch06/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch06/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch06/src/web/pages/plants/displayPlant.jsp b/ch06/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch06/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch06/src/web/pages/plants/displayPlantBody.jsp b/ch06/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch06/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch06/src/web/pages/plants/displayResults.jsp b/ch06/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch06/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch06/src/web/pages/plants/displayResultsBody.jsp b/ch06/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch06/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch06/src/web/pages/plants/index.jsp b/ch06/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch06/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch06/src/web/pages/plants/searchForm.jsp b/ch06/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch06/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch06/src/web/pages/template.jsp b/ch06/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch06/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch06/src/web/tags/simple.tag b/ch06/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch06/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch07/build.properties b/ch07/build.properties new file mode 100644 index 0000000..3471d11 --- /dev/null +++ b/ch07/build.properties @@ -0,0 +1,110 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +database.root=jdbc:mysql://localhost:3306/ +driver.name=com.mysql.jdbc.Driver + +drop.sql=SQL/drop.sql +create.sql=SQL/create.sql +insert.sql=SQL/insert.sql + +# The name of the Tomcat start script +tomcat.executableName=startup diff --git a/ch07/build.xml b/ch07/build.xml new file mode 100644 index 0000000..a8d9591 --- /dev/null +++ b/ch07/build.xml @@ -0,0 +1,1024 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${mail.message.docs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch07/src/shared/conf/SQL/plants.sql b/ch07/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch07/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch07/src/shared/conf/SQL/plantsDelete.sql b/ch07/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch07/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch07/src/shared/conf/SQL/plantsDrop.sql b/ch07/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch07/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch07/src/shared/conf/SQL/plantsInsert.sql b/ch07/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch07/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch07/src/shared/conf/database.properties b/ch07/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch07/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch07/src/shared/docs/LICENSE b/ch07/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch07/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch07/src/shared/docs/README b/ch07/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch07/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch07/src/shared/docs/index.html b/ch07/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch07/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch07/src/shared/java/org/mwrm/plants/Constants.java b/ch07/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch07/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch07/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch07/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch07/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch07/src/shared/java/org/mwrm/plants/SelectData.java b/ch07/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch07/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch07/src/shared/java/org/mwrm/plants/package.html b/ch07/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch07/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch07/src/stand-alone/docs/index.html b/ch07/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch07/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch07/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch07/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch07/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch07/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch07/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch07/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch07/src/web/conf/antBook.xml b/ch07/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch07/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch07/src/web/conf/plant.tld b/ch07/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch07/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch07/src/web/conf/web.xml b/ch07/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch07/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch07/src/web/docs/index.html b/ch07/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch07/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch07/src/web/images/banner_under.jpg b/ch07/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch07/src/web/images/banner_under.jpg differ diff --git a/ch07/src/web/images/cyc_cou.jpg b/ch07/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch07/src/web/images/cyc_cou.jpg differ diff --git a/ch07/src/web/images/gal_niv.jpg b/ch07/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch07/src/web/images/gal_niv.jpg differ diff --git a/ch07/src/web/images/no_image.jpg b/ch07/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch07/src/web/images/no_image.jpg differ diff --git a/ch07/src/web/images/ran_fic.jpg b/ch07/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch07/src/web/images/ran_fic.jpg differ diff --git a/ch07/src/web/images/tul_tur.jpg b/ch07/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch07/src/web/images/tul_tur.jpg differ diff --git a/ch07/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch07/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch07/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch07/src/web/java/org/mwrm/plants/servlets/package.html b/ch07/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch07/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch07/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch07/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch07/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch07/src/web/java/org/mwrm/plants/tags/package.html b/ch07/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch07/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch07/src/web/pages/footer.html b/ch07/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch07/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch07/src/web/pages/header.jsp b/ch07/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch07/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch07/src/web/pages/index.jsp b/ch07/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch07/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch07/src/web/pages/indexBody.jsp b/ch07/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch07/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch07/src/web/pages/menu.jsp b/ch07/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch07/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch07/src/web/pages/plantStyle.css b/ch07/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch07/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch07/src/web/pages/plants/displayPlant.jsp b/ch07/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch07/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch07/src/web/pages/plants/displayPlantBody.jsp b/ch07/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch07/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch07/src/web/pages/plants/displayResults.jsp b/ch07/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch07/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch07/src/web/pages/plants/displayResultsBody.jsp b/ch07/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch07/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch07/src/web/pages/plants/index.jsp b/ch07/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch07/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch07/src/web/pages/plants/searchForm.jsp b/ch07/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch07/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch07/src/web/pages/template.jsp b/ch07/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch07/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch07/src/web/tags/simple.tag b/ch07/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch07/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch08/build.properties b/ch08/build.properties new file mode 100644 index 0000000..d7ea7fd --- /dev/null +++ b/ch08/build.properties @@ -0,0 +1,130 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +database.root=jdbc:mysql://localhost:3306/ +driver.name=com.mysql.jdbc.Driver + +drop.sql=SQL/drop.sql +create.sql=SQL/create.sql +insert.sql=SQL/insert.sql + +# The name of the Tomcat start script +tomcat.executableName=startup + +# The test directory +test.src=test +test.build=${build}/test +test.junit.reports=${test.build}/reports/junit +test.junit.data=${test.build}/data/junit +test.junit.style=${test.src}/style/junit + +junit.home=C:/junit3.8.1 +junit.jar=${junit.home}/junit.jar + +httpunit.home=C:/httpunit +httpunit.jar=${httpunit.home}/lib/httpunit.jar + +last.test.failed.file=failed.txt + +checkstyle.home=C:/checkstyle +checkstyle.jar=${checkstyle.home}/checkstyle-3.5.jar +test.checkstyle.reports=${test.build}/reports/checkstyle +test.checkstyle.data=${test.build}/data/checkstyle diff --git a/ch08/build.xml b/ch08/build.xml new file mode 100644 index 0000000..9d97f06 --- /dev/null +++ b/ch08/build.xml @@ -0,0 +1,1344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${mail.message.docs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch08/src/shared/conf/SQL/plants.sql b/ch08/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch08/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch08/src/shared/conf/SQL/plantsDelete.sql b/ch08/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch08/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch08/src/shared/conf/SQL/plantsDrop.sql b/ch08/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch08/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch08/src/shared/conf/SQL/plantsInsert.sql b/ch08/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch08/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch08/src/shared/conf/database.properties b/ch08/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch08/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch08/src/shared/docs/LICENSE b/ch08/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch08/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch08/src/shared/docs/README b/ch08/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch08/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch08/src/shared/docs/index.html b/ch08/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch08/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch08/src/shared/java/org/mwrm/plants/Constants.java b/ch08/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch08/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch08/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch08/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch08/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch08/src/shared/java/org/mwrm/plants/SelectData.java b/ch08/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch08/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch08/src/shared/java/org/mwrm/plants/package.html b/ch08/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch08/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch08/src/stand-alone/docs/index.html b/ch08/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch08/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch08/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch08/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch08/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch08/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch08/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch08/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch08/src/web/conf/antBook.xml b/ch08/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch08/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch08/src/web/conf/plant.tld b/ch08/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch08/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch08/src/web/conf/web.xml b/ch08/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch08/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch08/src/web/docs/index.html b/ch08/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch08/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch08/src/web/images/banner_under.jpg b/ch08/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch08/src/web/images/banner_under.jpg differ diff --git a/ch08/src/web/images/cyc_cou.jpg b/ch08/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch08/src/web/images/cyc_cou.jpg differ diff --git a/ch08/src/web/images/gal_niv.jpg b/ch08/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch08/src/web/images/gal_niv.jpg differ diff --git a/ch08/src/web/images/no_image.jpg b/ch08/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch08/src/web/images/no_image.jpg differ diff --git a/ch08/src/web/images/ran_fic.jpg b/ch08/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch08/src/web/images/ran_fic.jpg differ diff --git a/ch08/src/web/images/tul_tur.jpg b/ch08/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch08/src/web/images/tul_tur.jpg differ diff --git a/ch08/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch08/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch08/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch08/src/web/java/org/mwrm/plants/servlets/package.html b/ch08/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch08/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch08/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch08/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch08/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch08/src/web/java/org/mwrm/plants/tags/package.html b/ch08/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch08/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch08/src/web/pages/footer.html b/ch08/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch08/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch08/src/web/pages/header.jsp b/ch08/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch08/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch08/src/web/pages/index.jsp b/ch08/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch08/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch08/src/web/pages/indexBody.jsp b/ch08/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch08/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch08/src/web/pages/menu.jsp b/ch08/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch08/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch08/src/web/pages/plantStyle.css b/ch08/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch08/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch08/src/web/pages/plants/displayPlant.jsp b/ch08/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch08/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch08/src/web/pages/plants/displayPlantBody.jsp b/ch08/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch08/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch08/src/web/pages/plants/displayResults.jsp b/ch08/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch08/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch08/src/web/pages/plants/displayResultsBody.jsp b/ch08/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch08/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch08/src/web/pages/plants/index.jsp b/ch08/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch08/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch08/src/web/pages/plants/searchForm.jsp b/ch08/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch08/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch08/src/web/pages/template.jsp b/ch08/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch08/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch08/src/web/tags/simple.tag b/ch08/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch08/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch08/test/org/mwrm/plants/PlantWebTest.java b/ch08/test/org/mwrm/plants/PlantWebTest.java new file mode 100644 index 0000000..f1c5093 --- /dev/null +++ b/ch08/test/org/mwrm/plants/PlantWebTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.xml.sax.SAXException; + +import junit.framework.TestCase; + +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebResponse; + +/** + *

Tests the plant servlet. + * It checks that the web application is running + * and then checks the session is emptied + * if the there are no results in the query.

+ */ +public class PlantWebTest extends TestCase { + + /** The URL of the server. */ + private static final String SERVER_URL = "http://localhost:8080"; + + /** The web application's name. */ + private static final String WEB_APP = "/antBook"; + + /** The response code we're looking for. */ + private static final int RESPONSE_CODE = 200; + + /** + *

The constructor, + * which simply calls super(name).

+ * @param name The name of the test + */ + public PlantWebTest(final String name) { + super(name); + } + + /** + *

We want to make sure that the web application is running.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testIsRunning() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + try { + // Send a request to the web application's root + WebResponse resp = wc.getResponse(SERVER_URL + WEB_APP); + + // If there is a 200 return code, then it is available + assertEquals("Web application not available at " + + SERVER_URL + WEB_APP, + RESPONSE_CODE, resp.getResponseCode()); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } + + /** + *

The application should detect that no results have been obtained.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testSession() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + + try { + // First send a request that will not produce any results + WebResponse resp = + wc.getResponse(SERVER_URL + WEB_APP + + "/plants/listPlants.jsp?show=name&letter=X"); + // Check that this is the case + assertTrue("Session not cancelled after empty results", + (resp.getText().indexOf("Sorry") > -1)); + + // Now go to the index page, + // where there should not be an error message + resp = wc.getResponse(SERVER_URL + WEB_APP + "/plants/"); + assertTrue("Session not cancelled after empty results", + !(resp.getText().indexOf("Sorry") > -1)); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } +} diff --git a/ch08/test/org/mwrm/plants/package.html b/ch08/test/org/mwrm/plants/package.html new file mode 100644 index 0000000..a9c3f06 --- /dev/null +++ b/ch08/test/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants + + +

Contains the test classes for the plant application.

+ + diff --git a/ch09/build.jstl.xml b/ch09/build.jstl.xml new file mode 100644 index 0000000..491976d --- /dev/null +++ b/ch09/build.jstl.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch09/build.mysql.xml b/ch09/build.mysql.xml new file mode 100644 index 0000000..98f2e76 --- /dev/null +++ b/ch09/build.mysql.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch09/build.package.xml b/ch09/build.package.xml new file mode 100644 index 0000000..dc98429 --- /dev/null +++ b/ch09/build.package.xml @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch09/build.properties b/ch09/build.properties new file mode 100644 index 0000000..3428e0d --- /dev/null +++ b/ch09/build.properties @@ -0,0 +1,136 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# The test directory +test.src=test +test.build=${build}/test +test.junit.reports=${test.build}/reports/junit +test.junit.data=${test.build}/data/junit +test.junit.style=${test.src}/style/junit +last.test.failed.file=failed.txt + +junit.home=C:/junit3.8.1 +junit.jar=${junit.home}/junit.jar + +httpunit.home=C:/httpunit-1.6 +httpunit.jar=${httpunit.home}/lib/httpunit.jar + +checkstyle.home=C:/checkstyle-3.5 +checkstyle.jar=${checkstyle.home}/checkstyle-3.5.jar +test.checkstyle.reports=${test.build}/reports/checkstyle +test.checkstyle.data=${test.build}/data/checkstyle + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +#servlet24.jar=${lib}/servlet-api.jar + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war +appName-test.jar=${dist}/${appName}-test.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the below if using Ant to download the JAR (see below) +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The directory where the docs will go +docs=${build}/docs +tmp=C:/TEMP/antBook + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the plant information application +javadoc.windowtitle=The plant information application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +# Properties for offline Javadoc generation +javadoc.j2se.offline=C:/j2sdk1.4.2/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +# Names of packaged distributions +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +# Email settings +mail.from=antBuild@example.com +mail.tolist=mattmoodie@yahoo.com +mail.message.docs=Here's the docs distribution +mail.message.src=Here's the source distribution +mail.message.bin=Here's the binary distribution +mail.message.all=Here's the whole distribution +mail.mailhost=smtp.mail.yahoo.co.uk +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The name of the Tomcat start script +tomcat.executableName=startup + +# Subordinate build file +build.shared.xml=${src.shared.root}/build.xml +build.stand-alone.xml=${src.stand-alone.root}/build.xml +build.web.xml=${src.web.root}/build.xml +build.package.xml=build.package.xml +build.test.xml=${test.src}/build.xml + +build.jstl.xml=build.jstl.xml +build.mysql.xml=build.mysql.xml diff --git a/ch09/build.xml b/ch09/build.xml new file mode 100644 index 0000000..ae92a81 --- /dev/null +++ b/ch09/build.xml @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch09/src/shared/conf/SQL/plants.sql b/ch09/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch09/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch09/src/shared/conf/SQL/plantsDelete.sql b/ch09/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch09/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch09/src/shared/conf/SQL/plantsDrop.sql b/ch09/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch09/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch09/src/shared/conf/SQL/plantsInsert.sql b/ch09/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch09/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch09/src/shared/conf/database.properties b/ch09/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch09/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch09/src/shared/docs/LICENSE b/ch09/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch09/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch09/src/shared/docs/README b/ch09/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch09/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch09/src/shared/docs/index.html b/ch09/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch09/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch09/src/shared/java/org/mwrm/plants/Constants.java b/ch09/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch09/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch09/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch09/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch09/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch09/src/shared/java/org/mwrm/plants/SelectData.java b/ch09/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch09/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch09/src/shared/java/org/mwrm/plants/package.html b/ch09/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch09/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch09/src/stand-alone/docs/index.html b/ch09/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch09/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch09/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch09/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch09/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch09/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch09/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch09/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch09/src/web/conf/antBook.xml b/ch09/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch09/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch09/src/web/conf/plant.tld b/ch09/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch09/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch09/src/web/conf/web.xml b/ch09/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch09/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch09/src/web/docs/index.html b/ch09/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch09/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch09/src/web/images/banner_under.jpg b/ch09/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch09/src/web/images/banner_under.jpg differ diff --git a/ch09/src/web/images/cyc_cou.jpg b/ch09/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch09/src/web/images/cyc_cou.jpg differ diff --git a/ch09/src/web/images/gal_niv.jpg b/ch09/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch09/src/web/images/gal_niv.jpg differ diff --git a/ch09/src/web/images/no_image.jpg b/ch09/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch09/src/web/images/no_image.jpg differ diff --git a/ch09/src/web/images/ran_fic.jpg b/ch09/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch09/src/web/images/ran_fic.jpg differ diff --git a/ch09/src/web/images/tul_tur.jpg b/ch09/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch09/src/web/images/tul_tur.jpg differ diff --git a/ch09/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch09/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch09/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch09/src/web/java/org/mwrm/plants/servlets/package.html b/ch09/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch09/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch09/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch09/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch09/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch09/src/web/java/org/mwrm/plants/tags/package.html b/ch09/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch09/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch09/src/web/pages/footer.html b/ch09/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch09/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch09/src/web/pages/header.jsp b/ch09/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch09/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch09/src/web/pages/index.jsp b/ch09/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch09/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch09/src/web/pages/indexBody.jsp b/ch09/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch09/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch09/src/web/pages/menu.jsp b/ch09/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch09/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch09/src/web/pages/plantStyle.css b/ch09/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch09/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch09/src/web/pages/plants/displayPlant.jsp b/ch09/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch09/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch09/src/web/pages/plants/displayPlantBody.jsp b/ch09/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch09/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch09/src/web/pages/plants/displayResults.jsp b/ch09/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch09/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch09/src/web/pages/plants/displayResultsBody.jsp b/ch09/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch09/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch09/src/web/pages/plants/index.jsp b/ch09/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch09/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch09/src/web/pages/plants/searchForm.jsp b/ch09/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch09/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch09/src/web/pages/template.jsp b/ch09/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch09/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch09/src/web/tags/simple.tag b/ch09/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch09/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch09/test/build.xml b/ch09/test/build.xml new file mode 100644 index 0000000..78360fc --- /dev/null +++ b/ch09/test/build.xml @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch09/test/org/mwrm/plants/PlantWebTest.java b/ch09/test/org/mwrm/plants/PlantWebTest.java new file mode 100644 index 0000000..f1c5093 --- /dev/null +++ b/ch09/test/org/mwrm/plants/PlantWebTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.xml.sax.SAXException; + +import junit.framework.TestCase; + +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebResponse; + +/** + *

Tests the plant servlet. + * It checks that the web application is running + * and then checks the session is emptied + * if the there are no results in the query.

+ */ +public class PlantWebTest extends TestCase { + + /** The URL of the server. */ + private static final String SERVER_URL = "http://localhost:8080"; + + /** The web application's name. */ + private static final String WEB_APP = "/antBook"; + + /** The response code we're looking for. */ + private static final int RESPONSE_CODE = 200; + + /** + *

The constructor, + * which simply calls super(name).

+ * @param name The name of the test + */ + public PlantWebTest(final String name) { + super(name); + } + + /** + *

We want to make sure that the web application is running.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testIsRunning() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + try { + // Send a request to the web application's root + WebResponse resp = wc.getResponse(SERVER_URL + WEB_APP); + + // If there is a 200 return code, then it is available + assertEquals("Web application not available at " + + SERVER_URL + WEB_APP, + RESPONSE_CODE, resp.getResponseCode()); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } + + /** + *

The application should detect that no results have been obtained.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testSession() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + + try { + // First send a request that will not produce any results + WebResponse resp = + wc.getResponse(SERVER_URL + WEB_APP + + "/plants/listPlants.jsp?show=name&letter=X"); + // Check that this is the case + assertTrue("Session not cancelled after empty results", + (resp.getText().indexOf("Sorry") > -1)); + + // Now go to the index page, + // where there should not be an error message + resp = wc.getResponse(SERVER_URL + WEB_APP + "/plants/"); + assertTrue("Session not cancelled after empty results", + !(resp.getText().indexOf("Sorry") > -1)); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } +} diff --git a/ch09/test/org/mwrm/plants/package.html b/ch09/test/org/mwrm/plants/package.html new file mode 100644 index 0000000..a9c3f06 --- /dev/null +++ b/ch09/test/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants + + +

Contains the test classes for the plant application.

+ + diff --git a/ch10/ant/org/mwrm/ant/tasks/ClassSetTask.java b/ch10/ant/org/mwrm/ant/tasks/ClassSetTask.java new file mode 100644 index 0000000..5afbdfd --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/ClassSetTask.java @@ -0,0 +1,60 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

The ClassSetTask class + * demonstrates how to use a Class argument + * in a custom class attribute.

+ * + */ + +public class ClassSetTask extends Task { + + /** The qualified class name. */ + private Class qualifiedName; + + /** + *

Runs the task and displays the qualified name of the class + * that is set as the setQualifiedName attribute.

+ * + */ + public final void execute() { + log("qualifiedName: " + qualifiedName, Project.MSG_INFO); + } + + /** + *

Sets the fully qualified name of the class.

+ ** @param qName The fully qualified name of a class + */ + public final void setQualifiedName(final Class qName) { + if (qName.getName().equals("java.lang.Integer") + || + qName.getName().equals("java.lang.String")) { + log(qName.getName() + " found.", Project.MSG_INFO); + } else { + String msg = "You can only specify java.lang.Integer " + + "or java.lang.String in qualifiedName."; + throw new BuildException(msg); + } + this.qualifiedName = qName; + } +} + diff --git a/ch10/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java b/ch10/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java new file mode 100644 index 0000000..e9c076c --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java @@ -0,0 +1,188 @@ +/* + * Extends org.apache.tools.ant.taskdefs.Javadoc + * and uses org.apache.tools.ant.taskdefs.UpToDate, + * which are Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import java.io.File; + +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; + +import org.apache.tools.ant.taskdefs.Javadoc; +import org.apache.tools.ant.taskdefs.UpToDate; + +import org.apache.tools.ant.types.FileSet; + +/** + *

The ExtendJavadocTask class + * extends the {@link org.apache.tools.ant.taskdefs.Javadoc Javadoc} task. + * It checks whether a set of source files are newer than a set of target files + * and if so, it generates Javadocs.

+ * + */ + +public class ExtendJavadocTask extends Javadoc { + + /** The attribute of the task element. */ + private File target; + + /** A set of file sets, + * each of which is provided by a nested file set. */ + private Vector targetFileSets = new Vector(); + + /** The internal uptodate task. */ + private UpToDate utd; + + /** + *

Creates a new instance of an internal + * <uptodate> task + * and adds it to the current project.

+ * @see UpToDate org.apache.tools.ant.taskdefs.UpToDate + */ + public final void init() { + // We need an instance of the task + utd = new UpToDate(); + // We need to add the task to this project + utd.setProject(this.getProject()); + } + + /** + *

Checks if Javadocs should be created + * and then calls super.execute() if so.

+ *

This method does usage checks on the task's attributes + * and its nested elements. + * It will throw a BuildException if there is a violation.

+ */ + public final void execute() { + // This is the usage information + + // We can't have a target attribute + // and nested targetfiles elements + if (target != null && targetFileSets.size() > 0) { + String msg = "You can't specify a targetfile attribute " + + "and elements."; + throw new BuildException(msg); + } + // We have to specify either a target attribute + // or at least one nested targetfiles elements + if (target == null && targetFileSets.size() == 0) { + String msg = "You must specify either a targetfile attribute " + + "or at least one element."; + throw new BuildException(msg); + } + + // If this is false, the files aren't up to date + // and we have to run the javadocs + boolean eval = false; + + // If we've got to this point, we know the usage must be correct. + // Let's check whether a single target attribute has been used. + + if (target != null) { + // Get the results of the check + eval = getResult(target); + } else { + // If a target attribute wasn't specified, + // at least one nested targetfiles element was. + + // We first get all the file sets represented by the nested elements + Enumeration e = targetFileSets.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + + // The next element is a file set, so we get its files + // in a semi-colon-separated list + String files = e.nextElement().toString(); + + // Next, we split the list into its file names + StringTokenizer st = new StringTokenizer(files, ";"); + + // And iterate over them to test each one + while (st.hasMoreTokens()) { + // We create a file + //from the current file name in the iteration + File tempTarget = new File(st.nextToken()); + + // Get the results of the check + eval = getResult(tempTarget); + + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + } + + // If the test failed, we want to generate Javadocs + if (!eval) { + super.execute(); + } else { + log("Skipping Javadoc creation. The files are up to date.", + Project.MSG_INFO); + } + } + + /**

Checks whether the files are up to date.

+ * @param file The file to evaluate + * @return boolean The result + */ + private boolean getResult(final File file) { + // Set the target property in the task + utd.setTargetFile(file); + // Evaluate the files + return utd.eval(); + } + + /** + *

The setter method for the target attribute.

+ * @param targetFile A file to check against + */ + public final void setTarget(final File targetFile) { + this.target = targetFile; + } + + /** + *

The setter method for the file set + * contained in the nested <srcfiles> element.

+ * @param fileset A file set of source files + */ + public final void addSrcfiles(final FileSet fileset) { + utd.addSrcfiles(fileset); + } + + /** + *

The setter method for the file sets + * contained in nested <targetfiles> elements.

+ * @param fileset A file set of target files + */ + public final void addTargetfiles(final FileSet fileset) { + targetFileSets.add(fileset); + } +} + diff --git a/ch10/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java b/ch10/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java new file mode 100644 index 0000000..bfd1215 --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java @@ -0,0 +1,347 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ * + *

This class demonstrates how to nest elements within a custom task. + * Its nested element is called <name> + * and cannot be used in conjunction with a name attribute.

+ */ +public class LifeCycleNestedTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** The collection of name elements. */ + private Vector nameElements = new Vector(); + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleNestedTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + this.name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name != null && nameElements.size() > 0) { + String msg = "You can't specify a name attribute " + + "and elements."; + throw new BuildException(msg); + } + if (name == null && nameElements.size() == 0) { + String msg = "You must specify either a name attribute " + + "or at least one element."; + throw new BuildException(msg); + } + if (nameElements.size() > 0 && text != null) { + String msg = "You can't specify elements " + + "and body text."; + throw new BuildException(msg); + } + + logAll("execute()"); + + // If name is not set, we want to check nested elements + if (name == null) { + // Get the name elements + Enumeration e = nameElements.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + NameElement nameElement = (NameElement) e.nextElement(); + + // Usage check + if (nameElement.getName() == null) { + String msg = "You must specify a name attribute " + + "or body text for a nested element."; + throw new BuildException(msg); + } + log("Value of name element: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + log("---------------", Project.MSG_VERBOSE); + } + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see if this object is a custom task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + Task aTask = (Task) obj; + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + + // If name is set, we only have one value to print + if (name != null) { + log("Value of name attribute: " + this.name, Project.MSG_VERBOSE); + } + + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + + " at line " + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + // + " and column " + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addName()"); + log("Value of this name: " + + nameElement.getName(), Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addConfiguredName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addConfiguredName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized. + * In this case, the createName() method + * has the responsibility + * for creating the object.

+ * @see LifeCycleNestedTask.NameElement + */ + public final NameElement createName() { + NameElement nameElement = new NameElement("Madeleine"); + + nameElements.add(nameElement); + + logAll("createName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + + return nameElement; + } + + /** + *

A class that implements + * the nested <name> element + * of a LifeCycleNestedTask. + * @see LifeCycleNestedTask + */ + public static class NameElement { + + /** The name attribute of this element. */ + private String name; + + /** Tells the class if we've used the overridden constructor. */ + private boolean usedConstructor = false; + + /** The empty constructor. */ + public NameElement() { + // Empty + } + + /** + *

Used by the LifeCycleNestedTask.createName() method + * to created a nested <name> element.

+ * @see LifeCycleNestedTask#createName() + */ + public NameElement(final String text) { + this.name = text; + usedConstructor = true; + } + + /** + *

The mutator method for the name attribute.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + this.name = aName; + } + + /** + *

The accessor method for the name attribute.

+ * @return String The name to display + */ + public final String getName() { + return name; + } + + /** + *

Sets the body text of the <name> element. + * It contains a usage check.

+ * @param text The body text + */ + public final void addText(final String text) { + // Usage check + if (name != null && !usedConstructor) { + String msg = "You can't specify a name attribute " + + "and nested text in elements."; + throw new BuildException(msg); + } else { + this.name = text.trim(); + } + } + } +} + diff --git a/ch10/ant/org/mwrm/ant/tasks/LifeCycleTask.java b/ch10/ant/org/mwrm/ant/tasks/LifeCycleTask.java new file mode 100644 index 0000000..51c5219 --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/LifeCycleTask.java @@ -0,0 +1,194 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ */ +public class LifeCycleTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name == null) { + throw new BuildException("You must specify a name attribute in " + + getTaskName() + "."); + } + logAll("execute()"); + + // Write the name to output + log(name, Project.MSG_INFO); + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see whether this object is a task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + + Task aTask = (Task) obj; + + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + log("Value of name attribute: " + name, Project.MSG_VERBOSE); + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + " at line " + + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + " and column " + // + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } +} + diff --git a/ch10/ant/org/mwrm/ant/tasks/ProjectHelpTask.java b/ch10/ant/org/mwrm/ant/tasks/ProjectHelpTask.java new file mode 100644 index 0000000..32172b0 --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/ProjectHelpTask.java @@ -0,0 +1,72 @@ +/* + * Uses org.apache.tools.ant.Main, + * which is Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Main; + +/** + *

The ProjectHelpTask class displays usage information + * for the current project. This is the same information as is displayed + * by -projecthelp.

+ * + */ + +public class ProjectHelpTask extends Task { + + /** The location of the build file to use + * when obtaining usage information. */ + private String buildfile; + + /** + *

Runs the task. + * It calls {@link org.apache.tools.ant.Main#main(String[] args) + * org.apache.tools.ant.Main.main()} with the -projecthelp + * parameter. It will also send the current build file's file name + * via the -f parameter.

+ * + *

The buildfile attribute is optional. + * The default is the task's build file.

+ * @see org.apache.tools.ant.Main org.apache.tools.ant.Main + */ + public final void execute() { + // If the buildfile attribute is null, we'll use the task's build file + if (buildfile == null) { + buildfile = getLocation().getFileName(); + } + + // The arguments that we will pass to the Main class. + // The buildfile attribute must follow the -f parameter. + String[] args = {"-projecthelp", "-f", buildfile}; + + // Call the Main Ant class with the arguments. + Main.main(args); + } + + /** + * Setter method for the buildfile attribute. + * @param file The file name of the build file to use. + * + */ + public final void setBuildfile(final String file) { + this.buildfile = file; + } + +} + diff --git a/ch10/ant/org/mwrm/ant/tasks/package.html b/ch10/ant/org/mwrm/ant/tasks/package.html new file mode 100644 index 0000000..26003dc --- /dev/null +++ b/ch10/ant/org/mwrm/ant/tasks/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.tasks Package + + +

A collection of classes that demonstrate the Ant task life cycle.

+ + diff --git a/ch10/antBook.antlib.xml b/ch10/antBook.antlib.xml new file mode 100644 index 0000000..7927073 --- /dev/null +++ b/ch10/antBook.antlib.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch10/antlib.xml b/ch10/antlib.xml new file mode 100644 index 0000000..72d6f9a --- /dev/null +++ b/ch10/antlib.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch10/build.properties b/ch10/build.properties new file mode 100644 index 0000000..8337dd7 --- /dev/null +++ b/ch10/build.properties @@ -0,0 +1,139 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +database.root=jdbc:mysql://localhost:3306/ +driver.name=com.mysql.jdbc.Driver + +drop.sql=SQL/drop.sql +create.sql=SQL/create.sql +insert.sql=SQL/insert.sql + +# The name of the Tomcat start script +tomcat.executableName=startup + +# The test directory +test.src=test +test.build=${build}/test +test.junit.reports=${test.build}/reports/junit +test.junit.data=${test.build}/data/junit +test.junit.style=${test.src}/style/junit + +junit.home=C:/junit3.8.1 +junit.jar=${junit.home}/junit.jar + +httpunit.home=C:/httpunit +httpunit.jar=${httpunit.home}/lib/httpunit.jar + +last.test.failed.file=failed.txt + +checkstyle.home=C:/checkstyle +checkstyle.jar=${checkstyle.home}/checkstyle-3.5.jar +test.checkstyle.reports=${test.build}/reports/checkstyle +test.checkstyle.data=${test.build}/data/checkstyle + +# Custom task properties +ant.tasks.src=ant +ant.tasks.build=${build}/ant +ant.tasks.jar=${dist}/${appName}-tasks.jar + +ant.tasks.antlib.xml=antlib.xml +ant.tasks.antlib.package=org/mwrm/ant +ant.tasks.antlib.dir=${ant.tasks.build}/${ant.tasks.antlib.package} \ No newline at end of file diff --git a/ch10/build.xml b/ch10/build.xml new file mode 100644 index 0000000..474b28e --- /dev/null +++ b/ch10/build.xml @@ -0,0 +1,1468 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${mail.message.docs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + diff --git a/ch10/src/shared/conf/SQL/plants.sql b/ch10/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch10/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch10/src/shared/conf/SQL/plantsDelete.sql b/ch10/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch10/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch10/src/shared/conf/SQL/plantsDrop.sql b/ch10/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch10/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch10/src/shared/conf/SQL/plantsInsert.sql b/ch10/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch10/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch10/src/shared/conf/database.properties b/ch10/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch10/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch10/src/shared/docs/LICENSE b/ch10/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch10/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch10/src/shared/docs/README b/ch10/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch10/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch10/src/shared/docs/index.html b/ch10/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch10/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch10/src/shared/java/org/mwrm/plants/Constants.java b/ch10/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch10/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch10/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch10/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch10/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch10/src/shared/java/org/mwrm/plants/SelectData.java b/ch10/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch10/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch10/src/shared/java/org/mwrm/plants/package.html b/ch10/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch10/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch10/src/stand-alone/docs/index.html b/ch10/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch10/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch10/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch10/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch10/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch10/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch10/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch10/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch10/src/web/conf/antBook.xml b/ch10/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch10/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch10/src/web/conf/plant.tld b/ch10/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch10/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch10/src/web/conf/web.xml b/ch10/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch10/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch10/src/web/docs/index.html b/ch10/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch10/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch10/src/web/images/banner_under.jpg b/ch10/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch10/src/web/images/banner_under.jpg differ diff --git a/ch10/src/web/images/cyc_cou.jpg b/ch10/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch10/src/web/images/cyc_cou.jpg differ diff --git a/ch10/src/web/images/gal_niv.jpg b/ch10/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch10/src/web/images/gal_niv.jpg differ diff --git a/ch10/src/web/images/no_image.jpg b/ch10/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch10/src/web/images/no_image.jpg differ diff --git a/ch10/src/web/images/ran_fic.jpg b/ch10/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch10/src/web/images/ran_fic.jpg differ diff --git a/ch10/src/web/images/tul_tur.jpg b/ch10/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch10/src/web/images/tul_tur.jpg differ diff --git a/ch10/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch10/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch10/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch10/src/web/java/org/mwrm/plants/servlets/package.html b/ch10/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch10/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch10/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch10/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch10/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch10/src/web/java/org/mwrm/plants/tags/package.html b/ch10/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch10/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch10/src/web/pages/footer.html b/ch10/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch10/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch10/src/web/pages/header.jsp b/ch10/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch10/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
+

Plant application

+

+ + + + + + + ${letter} + + +

+
\ No newline at end of file diff --git a/ch10/src/web/pages/index.jsp b/ch10/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch10/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch10/src/web/pages/indexBody.jsp b/ch10/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch10/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch10/src/web/pages/menu.jsp b/ch10/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch10/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch10/src/web/pages/plantStyle.css b/ch10/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch10/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch10/src/web/pages/plants/displayPlant.jsp b/ch10/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch10/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch10/src/web/pages/plants/displayPlantBody.jsp b/ch10/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch10/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch10/src/web/pages/plants/displayResults.jsp b/ch10/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch10/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch10/src/web/pages/plants/displayResultsBody.jsp b/ch10/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch10/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch10/src/web/pages/plants/index.jsp b/ch10/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch10/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch10/src/web/pages/plants/searchForm.jsp b/ch10/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch10/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch10/src/web/pages/template.jsp b/ch10/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch10/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch10/src/web/tags/simple.tag b/ch10/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch10/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch10/test/org/mwrm/plants/PlantWebTest.java b/ch10/test/org/mwrm/plants/PlantWebTest.java new file mode 100644 index 0000000..f1c5093 --- /dev/null +++ b/ch10/test/org/mwrm/plants/PlantWebTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.xml.sax.SAXException; + +import junit.framework.TestCase; + +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebResponse; + +/** + *

Tests the plant servlet. + * It checks that the web application is running + * and then checks the session is emptied + * if the there are no results in the query.

+ */ +public class PlantWebTest extends TestCase { + + /** The URL of the server. */ + private static final String SERVER_URL = "http://localhost:8080"; + + /** The web application's name. */ + private static final String WEB_APP = "/antBook"; + + /** The response code we're looking for. */ + private static final int RESPONSE_CODE = 200; + + /** + *

The constructor, + * which simply calls super(name).

+ * @param name The name of the test + */ + public PlantWebTest(final String name) { + super(name); + } + + /** + *

We want to make sure that the web application is running.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testIsRunning() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + try { + // Send a request to the web application's root + WebResponse resp = wc.getResponse(SERVER_URL + WEB_APP); + + // If there is a 200 return code, then it is available + assertEquals("Web application not available at " + + SERVER_URL + WEB_APP, + RESPONSE_CODE, resp.getResponseCode()); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } + + /** + *

The application should detect that no results have been obtained.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testSession() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + + try { + // First send a request that will not produce any results + WebResponse resp = + wc.getResponse(SERVER_URL + WEB_APP + + "/plants/listPlants.jsp?show=name&letter=X"); + // Check that this is the case + assertTrue("Session not cancelled after empty results", + (resp.getText().indexOf("Sorry") > -1)); + + // Now go to the index page, + // where there should not be an error message + resp = wc.getResponse(SERVER_URL + WEB_APP + "/plants/"); + assertTrue("Session not cancelled after empty results", + !(resp.getText().indexOf("Sorry") > -1)); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } +} diff --git a/ch10/test/org/mwrm/plants/package.html b/ch10/test/org/mwrm/plants/package.html new file mode 100644 index 0000000..a9c3f06 --- /dev/null +++ b/ch10/test/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants + + +

Contains the test classes for the plant application.

+ + diff --git a/ch11/ant/org/mwrm/ant/listeners/BuildEventListener.java b/ch11/ant/org/mwrm/ant/listeners/BuildEventListener.java new file mode 100644 index 0000000..15f3dd1 --- /dev/null +++ b/ch11/ant/org/mwrm/ant/listeners/BuildEventListener.java @@ -0,0 +1,116 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.listeners; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.BuildEvent; + +/** + *

A class that demonstrates some of the functionality + * of a custom listener.

+ */ +public class BuildEventListener implements BuildListener { + + /** + *

Signals that a build has started. This event + * is fired before any targets have started.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + */ + public final void buildStarted(final BuildEvent start) { + start.getProject().log("buildStarted() called.", Project.MSG_ERR); + } + + /** + *

Signals that the last target has finished. This event + * will still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void buildFinished(final BuildEvent finish) { + finish.getProject().log("buildFinished() called.", Project.MSG_ERR); + } + + /** + *

Signals that a target is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTarget() + */ + public final void targetStarted(final BuildEvent start) { + start.getProject().log("Target [" + start.getTarget().getName() + + "] started.", Project.MSG_ERR); + } + + /** + *

Signals that a target has finished. This event will + * still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void targetFinished(final BuildEvent finish) { + finish.getProject().log("Target [" + finish.getTarget().getName() + + "] finished.", Project.MSG_ERR); + } + + /** + *

Signals that a task is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTask() + */ + public final void taskStarted(final BuildEvent start) { + start.getProject().log("Task [" + start.getTask().getTaskName() + + "] started.", Project.MSG_ERR); + } + + /** + *

Signals that a task has finished. This event will still + * be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void taskFinished(final BuildEvent finish) { + finish.getProject().log("Task [" + finish.getTask().getTaskName() + + "] finished.", Project.MSG_ERR); + } + + /**

When a message is sent to this logger, Ant calls this method.

+ * @param event An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public void messageLogged(final BuildEvent event) { + // empty + } +} diff --git a/ch11/ant/org/mwrm/ant/listeners/package.html b/ch11/ant/org/mwrm/ant/listeners/package.html new file mode 100644 index 0000000..c12041d --- /dev/null +++ b/ch11/ant/org/mwrm/ant/listeners/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.listeners Package + + +

A package that contains custom listeners.

+ + diff --git a/ch11/ant/org/mwrm/ant/loggers/BuildEventLogger.java b/ch11/ant/org/mwrm/ant/loggers/BuildEventLogger.java new file mode 100644 index 0000000..60eb52c --- /dev/null +++ b/ch11/ant/org/mwrm/ant/loggers/BuildEventLogger.java @@ -0,0 +1,195 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.loggers; + +import java.io.PrintStream; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildLogger; +import org.apache.tools.ant.BuildEvent; + +/** + *

A class that demonstrates some of the functionality + * of a custom logger.

+ */ +public class BuildEventLogger implements BuildLogger { + + /** + * PrintStream to write non-error messages to. + */ + private PrintStream out; + + /** + * PrintStream to write error messages to. + */ + private PrintStream err; + + /** + * Sets whether to tailor output for Emacs, etc. + * The default is false. + */ + private boolean emacsMode = false; + + /** + * We'll set this logger to log only warnings. + */ + private int msgOutputLevel = Project.MSG_WARN; + + /** + *

Signals that a build has started. This event + * is fired before any targets have started.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + */ + public final void buildStarted(final BuildEvent start) { + start.getProject().log("Message from buildStarted().", Project.MSG_ERR); + } + + /** + *

Signals that the last target has finished. This event + * will still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void buildFinished(final BuildEvent finish) { + // empty + } + + /** + *

Signals that a target is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTarget() + */ + public void targetStarted(final BuildEvent start) { + // empty + } + + /** + *

Signals that a target has finished. This event will + * still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void targetFinished(final BuildEvent finish) { + // empty + } + + /** + *

Signals that a task is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTask() + */ + public void taskStarted(final BuildEvent start) { + // empty + } + + /** + *

Signals that a task has finished. This event will still + * be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void taskFinished(final BuildEvent finish) { + // empty + } + + /**

When a message is sent to this logger, Ant calls this method.

+ * @param event An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public final void messageLogged(final BuildEvent event) { + /// We need to determine how important this message is + int priority = event.getPriority(); + + // If it's as important as our log level, we display it + if (priority <= msgOutputLevel) { + out.println("messageLogged: " + event.getMessage()); + } + } + + /** + *

Sets the output stream to which this logger is to send its output.

+ * + * @param output The output stream for the logger. + * Must not be null. + */ + public final void setOutputPrintStream(final PrintStream output) { + this.out = new PrintStream(output, true); + } + + /** + *

Sets the output stream to which this logger + * is to send error messages.

+ * + * @param errorStream The error stream for the logger. + * Must not be null. + */ + public final void setErrorPrintStream(final PrintStream errorStream) { + this.err = new PrintStream(errorStream, true); + } + + /** + *

Sets this logger to produce Emacs + * (and other editor) friendly output.

+ * + * @param mode true if output is to be unadorned so that + * Emacs and other editors can parse files names, etc. + */ + public final void setEmacsMode(final boolean mode) { + this.emacsMode = mode; + } + + /** + *

Sets the highest level of message this logger should respond to.

+ * + *

Only messages with a message level lower than or equal to the + * given level should be written to the log.

+ *

+ * Constants for the message levels are in the + * {@link Project Project} class. The order of the levels, from least + * to most verbose, is MSG_ERR, MSG_WARN, + * MSG_INFO, MSG_VERBOSE, + * MSG_DEBUG.

+ *

The default for this logger is + * {@link Project#MSG_WARN Project.MSG_WARN}.

+ * + * @param level the logging level for the logger. + */ + public void setMessageOutputLevel(final int level) { + // We will leave this empty to use the default level, + // which we set above + } + +} diff --git a/ch11/ant/org/mwrm/ant/loggers/package.html b/ch11/ant/org/mwrm/ant/loggers/package.html new file mode 100644 index 0000000..e30bac9 --- /dev/null +++ b/ch11/ant/org/mwrm/ant/loggers/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.loggers Package + + +

A package that contains custom loggers.

+ + diff --git a/ch11/ant/org/mwrm/ant/tasks/ClassSetTask.java b/ch11/ant/org/mwrm/ant/tasks/ClassSetTask.java new file mode 100644 index 0000000..5afbdfd --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/ClassSetTask.java @@ -0,0 +1,60 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

The ClassSetTask class + * demonstrates how to use a Class argument + * in a custom class attribute.

+ * + */ + +public class ClassSetTask extends Task { + + /** The qualified class name. */ + private Class qualifiedName; + + /** + *

Runs the task and displays the qualified name of the class + * that is set as the setQualifiedName attribute.

+ * + */ + public final void execute() { + log("qualifiedName: " + qualifiedName, Project.MSG_INFO); + } + + /** + *

Sets the fully qualified name of the class.

+ ** @param qName The fully qualified name of a class + */ + public final void setQualifiedName(final Class qName) { + if (qName.getName().equals("java.lang.Integer") + || + qName.getName().equals("java.lang.String")) { + log(qName.getName() + " found.", Project.MSG_INFO); + } else { + String msg = "You can only specify java.lang.Integer " + + "or java.lang.String in qualifiedName."; + throw new BuildException(msg); + } + this.qualifiedName = qName; + } +} + diff --git a/ch11/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java b/ch11/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java new file mode 100644 index 0000000..e9c076c --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java @@ -0,0 +1,188 @@ +/* + * Extends org.apache.tools.ant.taskdefs.Javadoc + * and uses org.apache.tools.ant.taskdefs.UpToDate, + * which are Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import java.io.File; + +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; + +import org.apache.tools.ant.taskdefs.Javadoc; +import org.apache.tools.ant.taskdefs.UpToDate; + +import org.apache.tools.ant.types.FileSet; + +/** + *

The ExtendJavadocTask class + * extends the {@link org.apache.tools.ant.taskdefs.Javadoc Javadoc} task. + * It checks whether a set of source files are newer than a set of target files + * and if so, it generates Javadocs.

+ * + */ + +public class ExtendJavadocTask extends Javadoc { + + /** The attribute of the task element. */ + private File target; + + /** A set of file sets, + * each of which is provided by a nested file set. */ + private Vector targetFileSets = new Vector(); + + /** The internal uptodate task. */ + private UpToDate utd; + + /** + *

Creates a new instance of an internal + * <uptodate> task + * and adds it to the current project.

+ * @see UpToDate org.apache.tools.ant.taskdefs.UpToDate + */ + public final void init() { + // We need an instance of the task + utd = new UpToDate(); + // We need to add the task to this project + utd.setProject(this.getProject()); + } + + /** + *

Checks if Javadocs should be created + * and then calls super.execute() if so.

+ *

This method does usage checks on the task's attributes + * and its nested elements. + * It will throw a BuildException if there is a violation.

+ */ + public final void execute() { + // This is the usage information + + // We can't have a target attribute + // and nested targetfiles elements + if (target != null && targetFileSets.size() > 0) { + String msg = "You can't specify a targetfile attribute " + + "and elements."; + throw new BuildException(msg); + } + // We have to specify either a target attribute + // or at least one nested targetfiles elements + if (target == null && targetFileSets.size() == 0) { + String msg = "You must specify either a targetfile attribute " + + "or at least one element."; + throw new BuildException(msg); + } + + // If this is false, the files aren't up to date + // and we have to run the javadocs + boolean eval = false; + + // If we've got to this point, we know the usage must be correct. + // Let's check whether a single target attribute has been used. + + if (target != null) { + // Get the results of the check + eval = getResult(target); + } else { + // If a target attribute wasn't specified, + // at least one nested targetfiles element was. + + // We first get all the file sets represented by the nested elements + Enumeration e = targetFileSets.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + + // The next element is a file set, so we get its files + // in a semi-colon-separated list + String files = e.nextElement().toString(); + + // Next, we split the list into its file names + StringTokenizer st = new StringTokenizer(files, ";"); + + // And iterate over them to test each one + while (st.hasMoreTokens()) { + // We create a file + //from the current file name in the iteration + File tempTarget = new File(st.nextToken()); + + // Get the results of the check + eval = getResult(tempTarget); + + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + } + + // If the test failed, we want to generate Javadocs + if (!eval) { + super.execute(); + } else { + log("Skipping Javadoc creation. The files are up to date.", + Project.MSG_INFO); + } + } + + /**

Checks whether the files are up to date.

+ * @param file The file to evaluate + * @return boolean The result + */ + private boolean getResult(final File file) { + // Set the target property in the task + utd.setTargetFile(file); + // Evaluate the files + return utd.eval(); + } + + /** + *

The setter method for the target attribute.

+ * @param targetFile A file to check against + */ + public final void setTarget(final File targetFile) { + this.target = targetFile; + } + + /** + *

The setter method for the file set + * contained in the nested <srcfiles> element.

+ * @param fileset A file set of source files + */ + public final void addSrcfiles(final FileSet fileset) { + utd.addSrcfiles(fileset); + } + + /** + *

The setter method for the file sets + * contained in nested <targetfiles> elements.

+ * @param fileset A file set of target files + */ + public final void addTargetfiles(final FileSet fileset) { + targetFileSets.add(fileset); + } +} + diff --git a/ch11/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java b/ch11/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java new file mode 100644 index 0000000..bfd1215 --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java @@ -0,0 +1,347 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ * + *

This class demonstrates how to nest elements within a custom task. + * Its nested element is called <name> + * and cannot be used in conjunction with a name attribute.

+ */ +public class LifeCycleNestedTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** The collection of name elements. */ + private Vector nameElements = new Vector(); + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleNestedTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + this.name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name != null && nameElements.size() > 0) { + String msg = "You can't specify a name attribute " + + "and elements."; + throw new BuildException(msg); + } + if (name == null && nameElements.size() == 0) { + String msg = "You must specify either a name attribute " + + "or at least one element."; + throw new BuildException(msg); + } + if (nameElements.size() > 0 && text != null) { + String msg = "You can't specify elements " + + "and body text."; + throw new BuildException(msg); + } + + logAll("execute()"); + + // If name is not set, we want to check nested elements + if (name == null) { + // Get the name elements + Enumeration e = nameElements.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + NameElement nameElement = (NameElement) e.nextElement(); + + // Usage check + if (nameElement.getName() == null) { + String msg = "You must specify a name attribute " + + "or body text for a nested element."; + throw new BuildException(msg); + } + log("Value of name element: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + log("---------------", Project.MSG_VERBOSE); + } + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see if this object is a custom task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + Task aTask = (Task) obj; + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + + // If name is set, we only have one value to print + if (name != null) { + log("Value of name attribute: " + this.name, Project.MSG_VERBOSE); + } + + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + + " at line " + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + // + " and column " + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addName()"); + log("Value of this name: " + + nameElement.getName(), Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addConfiguredName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addConfiguredName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized. + * In this case, the createName() method + * has the responsibility + * for creating the object.

+ * @see LifeCycleNestedTask.NameElement + */ + public final NameElement createName() { + NameElement nameElement = new NameElement("Madeleine"); + + nameElements.add(nameElement); + + logAll("createName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + + return nameElement; + } + + /** + *

A class that implements + * the nested <name> element + * of a LifeCycleNestedTask. + * @see LifeCycleNestedTask + */ + public static class NameElement { + + /** The name attribute of this element. */ + private String name; + + /** Tells the class if we've used the overridden constructor. */ + private boolean usedConstructor = false; + + /** The empty constructor. */ + public NameElement() { + // Empty + } + + /** + *

Used by the LifeCycleNestedTask.createName() method + * to created a nested <name> element.

+ * @see LifeCycleNestedTask#createName() + */ + public NameElement(final String text) { + this.name = text; + usedConstructor = true; + } + + /** + *

The mutator method for the name attribute.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + this.name = aName; + } + + /** + *

The accessor method for the name attribute.

+ * @return String The name to display + */ + public final String getName() { + return name; + } + + /** + *

Sets the body text of the <name> element. + * It contains a usage check.

+ * @param text The body text + */ + public final void addText(final String text) { + // Usage check + if (name != null && !usedConstructor) { + String msg = "You can't specify a name attribute " + + "and nested text in elements."; + throw new BuildException(msg); + } else { + this.name = text.trim(); + } + } + } +} + diff --git a/ch11/ant/org/mwrm/ant/tasks/LifeCycleTask.java b/ch11/ant/org/mwrm/ant/tasks/LifeCycleTask.java new file mode 100644 index 0000000..51c5219 --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/LifeCycleTask.java @@ -0,0 +1,194 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ */ +public class LifeCycleTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name == null) { + throw new BuildException("You must specify a name attribute in " + + getTaskName() + "."); + } + logAll("execute()"); + + // Write the name to output + log(name, Project.MSG_INFO); + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see whether this object is a task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + + Task aTask = (Task) obj; + + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + log("Value of name attribute: " + name, Project.MSG_VERBOSE); + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + " at line " + + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + " and column " + // + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } +} + diff --git a/ch11/ant/org/mwrm/ant/tasks/ProjectHelpTask.java b/ch11/ant/org/mwrm/ant/tasks/ProjectHelpTask.java new file mode 100644 index 0000000..32172b0 --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/ProjectHelpTask.java @@ -0,0 +1,72 @@ +/* + * Uses org.apache.tools.ant.Main, + * which is Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Main; + +/** + *

The ProjectHelpTask class displays usage information + * for the current project. This is the same information as is displayed + * by -projecthelp.

+ * + */ + +public class ProjectHelpTask extends Task { + + /** The location of the build file to use + * when obtaining usage information. */ + private String buildfile; + + /** + *

Runs the task. + * It calls {@link org.apache.tools.ant.Main#main(String[] args) + * org.apache.tools.ant.Main.main()} with the -projecthelp + * parameter. It will also send the current build file's file name + * via the -f parameter.

+ * + *

The buildfile attribute is optional. + * The default is the task's build file.

+ * @see org.apache.tools.ant.Main org.apache.tools.ant.Main + */ + public final void execute() { + // If the buildfile attribute is null, we'll use the task's build file + if (buildfile == null) { + buildfile = getLocation().getFileName(); + } + + // The arguments that we will pass to the Main class. + // The buildfile attribute must follow the -f parameter. + String[] args = {"-projecthelp", "-f", buildfile}; + + // Call the Main Ant class with the arguments. + Main.main(args); + } + + /** + * Setter method for the buildfile attribute. + * @param file The file name of the build file to use. + * + */ + public final void setBuildfile(final String file) { + this.buildfile = file; + } + +} + diff --git a/ch11/ant/org/mwrm/ant/tasks/package.html b/ch11/ant/org/mwrm/ant/tasks/package.html new file mode 100644 index 0000000..26003dc --- /dev/null +++ b/ch11/ant/org/mwrm/ant/tasks/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.tasks Package + + +

A collection of classes that demonstrate the Ant task life cycle.

+ + diff --git a/ch11/antBook.antlib.xml b/ch11/antBook.antlib.xml new file mode 100644 index 0000000..7927073 --- /dev/null +++ b/ch11/antBook.antlib.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch11/antlib.xml b/ch11/antlib.xml new file mode 100644 index 0000000..72d6f9a --- /dev/null +++ b/ch11/antlib.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch11/build.properties b/ch11/build.properties new file mode 100644 index 0000000..e1a014e --- /dev/null +++ b/ch11/build.properties @@ -0,0 +1,144 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +database.root=jdbc:mysql://localhost:3306/ +driver.name=com.mysql.jdbc.Driver + +drop.sql=SQL/drop.sql +create.sql=SQL/create.sql +insert.sql=SQL/insert.sql + +# The name of the Tomcat start script +tomcat.executableName=startup + +# The test directory +test.src=test +test.build=${build}/test +test.junit.reports=${test.build}/reports/junit +test.junit.data=${test.build}/data/junit +test.junit.style=${test.src}/style/junit + +junit.home=C:/junit3.8.1 +junit.jar=${junit.home}/junit.jar + +httpunit.home=C:/httpunit +httpunit.jar=${httpunit.home}/lib/httpunit.jar + +last.test.failed.file=failed.txt + +checkstyle.home=C:/checkstyle +checkstyle.jar=${checkstyle.home}/checkstyle-3.5.jar +test.checkstyle.reports=${test.build}/reports/checkstyle +test.checkstyle.data=${test.build}/data/checkstyle + +# Custom task properties +ant.tasks.src=ant +ant.tasks.build=${build}/ant +ant.tasks.jar=${dist}/${appName}-tasks.jar + +ant.tasks.antlib.xml=antlib.xml +ant.tasks.antlib.package=org/mwrm/ant +ant.tasks.antlib.dir=${ant.tasks.build}/${ant.tasks.antlib.package} + +ant.api.jar=${dist}/${appName}-api.jar + +log4j.home=C:/logging-log4j-1.2.12 +log4j.jar=${log4j.home}/dist/lib/log4j.jar \ No newline at end of file diff --git a/ch11/build.xml b/ch11/build.xml new file mode 100644 index 0000000..0595862 --- /dev/null +++ b/ch11/build.xml @@ -0,0 +1,1595 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${mail.message.docs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jones + + + + + + + + + + + + + + + + + + + + + + + + ${converted} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + diff --git a/ch11/log.xml b/ch11/log.xml new file mode 100644 index 0000000..7c28a52 --- /dev/null +++ b/ch11/log.xml @@ -0,0 +1,14 @@ +java.lang.NullPointerException + at org.mwrm.ant.listeners.BuildEventListener.messageLogged(Unknown Source) + at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2009) + at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2029) + at org.apache.tools.ant.Project.log(Project.java:357) + at org.apache.tools.ant.Project.addReference(Project.java:1777) + at org.apache.tools.ant.PropertyHelper.getPropertyHelper(PropertyHelper.java:131) + at org.apache.tools.ant.Project.getProperty(Project.java:473) + at org.apache.tools.ant.XmlLogger.buildFinished(XmlLogger.java:166) + at org.apache.tools.ant.Project.fireBuildFinished(Project.java:1848) + at org.apache.tools.ant.Main.runBuild(Main.java:688) + at org.apache.tools.ant.Main.startAnt(Main.java:187) + at org.apache.tools.ant.launch.Launcher.run(Launcher.java:249) + at org.apache.tools.ant.launch.Launcher.main(Launcher.java:70) diff --git a/ch11/log.xsl b/ch11/log.xsl new file mode 100644 index 0000000..2272790 --- /dev/null +++ b/ch11/log.xsl @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + +
+ + http://jakarta.apache.org + + + Apache Ant +
+ + + +

+ + + + + + + +

+
+ Copyright © 2000-2002, Apache Software Foundation +
+
+ + +
+ + + + + + failed + complete + + + + + + + + + + + + + +
Build FailedBuild CompleteTotal Time:
+ +
+ See the stacktrace. +
+
+ + + + + +
ant.file
ant.version
java.version
os.name
+ +

Build events

+ + + + + + + +
targettaskmessage
+

+ + + +

Error details

+ + +
+
+
+ +

+
+ + + + + + + a + b + + + [ ] + + + + + + +
diff --git a/ch11/log4j.properties b/ch11/log4j.properties new file mode 100644 index 0000000..7fae6cf --- /dev/null +++ b/ch11/log4j.properties @@ -0,0 +1,94 @@ +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to a file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log + +# Use the simple layout +#log4j.appender.AntLogger.layout=org.apache.log4j.SimpleLayout + +#---------------------------------------------# +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to a pattern file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.pattern.log + +# Use a pattern layout +#log4j.appender.AntLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntLogger.layout.ConversionPattern=%d{ISO8601} : %p : %m %n + +#---------------------------------------------# +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to an HTML file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log.html + +# Set the layout to HTML and specify a title +#log4j.appender.AntLogger.layout=org.apache.log4j.HTMLLayout +#log4j.appender.AntLogger.layout.Title=Apress Ant Log + +#---------------------------------------------# +# Set the root logger for Ant +log4j.rootLogger=INFO, AntLogger + +# Log to the console +log4j.appender.AntLogger=org.apache.log4j.ConsoleAppender +log4j.appender.AntLogger.Target=System.out + +# Set a custom layout level +log4j.appender.AntLogger.layout=org.apache.log4j.PatternLayout +log4j.appender.AntLogger.layout.ConversionPattern=%d{ISO8601} : %p : %m %n + +#---------------------------------------------# +# Send all INFO messages and above to a file and +# all ERROR messages and above to the console +#log4j.rootLogger=INFO, AntINFO, AntERROR + +# Use a pattern file for the INFO messages +#log4j.appender.AntINFO=org.apache.log4j.FileAppender +#log4j.appender.AntINFO.File=C:/TEMP/antBook/logs/ant.pattern.log +#log4j.appender.AntINFO.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntINFO.layout.ConversionPattern=%d{ISO8601} : %r : %p : %m %n + +# Use an HTML file for ERROR messages +#log4j.appender.AntERROR=org.apache.log4j.FileAppender +#log4j.appender.AntERROR.File=C:/TEMP/antBook/logs/ant.log.html +#log4j.appender.AntERROR.layout=org.apache.log4j.HTMLLayout +#log4j.appender.AntERROR.layout.Title=Apress Error Log +#log4j.appender.AntERROR.Threshold=ERROR + +#---------------------------------------------# +# Use individual loggers for different components +#log4j.rootLogger=ERROR, AntLogger + +# Log to a file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log + +# Use the simple layout +#log4j.appender.AntLogger.layout=org.apache.log4j.SimpleLayout + +# Set a logger for project components +#log4j.logger.org.apache.tools.ant.Project=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.Target=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.UnknownElement=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.taskdefs=INFO,AntComponentLogger + +#log4j.appender.AntComponentLogger=org.apache.log4j.FileAppender +#log4j.appender.AntComponentLogger.File=C:/TEMP/antBook/logs/ant.pattern.log +#log4j.appender.AntComponentLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntComponentLogger.layout.ConversionPattern=%p: %m: %d{ISO8601} %n +#log4j.appender.AntComponentLogger.Threshold=INFO + +# Log to a file +#log4j.appender.AntComponentLogger=org.apache.log4j.FileAppender +#log4j.appender.AntComponentLogger.File=C:/TEMP/antBook/logs/ant.component.log + +# Use the pattern layout +#log4j.appender.AntComponentLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntComponentLogger.layout.ConversionPattern=%c: %m %n \ No newline at end of file diff --git a/ch11/mail.log.properties b/ch11/mail.log.properties new file mode 100644 index 0000000..badfd74 --- /dev/null +++ b/ch11/mail.log.properties @@ -0,0 +1,8 @@ +MailLogger.mailhost=smtp.mail.yahoo.co.uk +MailLogger.user=antBook +MailLogger.password=antB00k +MailLogger.from=ant.log@example.com +MailLogger.failure.to=ant.results@example.com +MailLogger.success.to=ant.results@example.com +MailLogger.failure.subject=Build failed +MailLogger.success.subject=Build succeeded diff --git a/ch11/src/shared/conf/SQL/plants.sql b/ch11/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch11/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch11/src/shared/conf/SQL/plantsDelete.sql b/ch11/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch11/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch11/src/shared/conf/SQL/plantsDrop.sql b/ch11/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch11/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch11/src/shared/conf/SQL/plantsInsert.sql b/ch11/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch11/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch11/src/shared/conf/database.properties b/ch11/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch11/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch11/src/shared/docs/LICENSE b/ch11/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch11/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch11/src/shared/docs/README b/ch11/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch11/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch11/src/shared/docs/index.html b/ch11/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch11/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch11/src/shared/java/org/mwrm/plants/Constants.java b/ch11/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch11/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch11/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch11/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch11/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch11/src/shared/java/org/mwrm/plants/SelectData.java b/ch11/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch11/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch11/src/shared/java/org/mwrm/plants/package.html b/ch11/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch11/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch11/src/stand-alone/docs/index.html b/ch11/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch11/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch11/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch11/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch11/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch11/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch11/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch11/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch11/src/web/conf/antBook.xml b/ch11/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch11/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch11/src/web/conf/plant.tld b/ch11/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch11/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch11/src/web/conf/web.xml b/ch11/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch11/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch11/src/web/docs/index.html b/ch11/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch11/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch11/src/web/images/banner_under.jpg b/ch11/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch11/src/web/images/banner_under.jpg differ diff --git a/ch11/src/web/images/cyc_cou.jpg b/ch11/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch11/src/web/images/cyc_cou.jpg differ diff --git a/ch11/src/web/images/gal_niv.jpg b/ch11/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch11/src/web/images/gal_niv.jpg differ diff --git a/ch11/src/web/images/no_image.jpg b/ch11/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch11/src/web/images/no_image.jpg differ diff --git a/ch11/src/web/images/ran_fic.jpg b/ch11/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch11/src/web/images/ran_fic.jpg differ diff --git a/ch11/src/web/images/tul_tur.jpg b/ch11/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch11/src/web/images/tul_tur.jpg differ diff --git a/ch11/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch11/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch11/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch11/src/web/java/org/mwrm/plants/servlets/package.html b/ch11/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch11/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch11/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch11/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch11/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch11/src/web/java/org/mwrm/plants/tags/package.html b/ch11/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch11/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch11/src/web/pages/footer.html b/ch11/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch11/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch11/src/web/pages/header.jsp b/ch11/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch11/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
\ No newline at end of file diff --git a/ch11/src/web/pages/index.jsp b/ch11/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch11/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch11/src/web/pages/indexBody.jsp b/ch11/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch11/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch11/src/web/pages/menu.jsp b/ch11/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch11/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch11/src/web/pages/plantStyle.css b/ch11/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch11/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch11/src/web/pages/plants/displayPlant.jsp b/ch11/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch11/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch11/src/web/pages/plants/displayPlantBody.jsp b/ch11/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch11/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch11/src/web/pages/plants/displayResults.jsp b/ch11/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch11/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch11/src/web/pages/plants/displayResultsBody.jsp b/ch11/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch11/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch11/src/web/pages/plants/index.jsp b/ch11/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch11/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch11/src/web/pages/plants/searchForm.jsp b/ch11/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch11/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch11/src/web/pages/template.jsp b/ch11/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch11/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch11/src/web/tags/simple.tag b/ch11/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch11/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch11/test/org/mwrm/plants/PlantWebTest.java b/ch11/test/org/mwrm/plants/PlantWebTest.java new file mode 100644 index 0000000..f1c5093 --- /dev/null +++ b/ch11/test/org/mwrm/plants/PlantWebTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.xml.sax.SAXException; + +import junit.framework.TestCase; + +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebResponse; + +/** + *

Tests the plant servlet. + * It checks that the web application is running + * and then checks the session is emptied + * if the there are no results in the query.

+ */ +public class PlantWebTest extends TestCase { + + /** The URL of the server. */ + private static final String SERVER_URL = "http://localhost:8080"; + + /** The web application's name. */ + private static final String WEB_APP = "/antBook"; + + /** The response code we're looking for. */ + private static final int RESPONSE_CODE = 200; + + /** + *

The constructor, + * which simply calls super(name).

+ * @param name The name of the test + */ + public PlantWebTest(final String name) { + super(name); + } + + /** + *

We want to make sure that the web application is running.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testIsRunning() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + try { + // Send a request to the web application's root + WebResponse resp = wc.getResponse(SERVER_URL + WEB_APP); + + // If there is a 200 return code, then it is available + assertEquals("Web application not available at " + + SERVER_URL + WEB_APP, + RESPONSE_CODE, resp.getResponseCode()); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } + + /** + *

The application should detect that no results have been obtained.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testSession() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + + try { + // First send a request that will not produce any results + WebResponse resp = + wc.getResponse(SERVER_URL + WEB_APP + + "/plants/listPlants.jsp?show=name&letter=X"); + // Check that this is the case + assertTrue("Session not cancelled after empty results", + (resp.getText().indexOf("Sorry") > -1)); + + // Now go to the index page, + // where there should not be an error message + resp = wc.getResponse(SERVER_URL + WEB_APP + "/plants/"); + assertTrue("Session not cancelled after empty results", + !(resp.getText().indexOf("Sorry") > -1)); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } +} diff --git a/ch11/test/org/mwrm/plants/package.html b/ch11/test/org/mwrm/plants/package.html new file mode 100644 index 0000000..a9c3f06 --- /dev/null +++ b/ch11/test/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants + + +

Contains the test classes for the plant application.

+ + diff --git a/ch12/ant/org/mwrm/ant/api/Copyer.java b/ch12/ant/org/mwrm/ant/api/Copyer.java new file mode 100644 index 0000000..bd74604 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/api/Copyer.java @@ -0,0 +1,100 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.api; + +import java.io.File; +import java.io.PrintStream; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.DefaultLogger; + +import org.apache.tools.ant.taskdefs.Copy; + +import org.apache.tools.ant.types.FileSet; + +/** + *

Uses a {@link FileSet FileSet} to implement a batch copy.

+ */ +public final class Copyer { + + /** + * The default constructor. + */ + private Copyer() { } + + /** + *

Copies all files that match the following patterns: + * *.xml and *.xsl.

+ * + * @param args Command-line arguments, + * though they have no effect in this class + * @see FileSet + */ + public static void main(final String[] args) { + // Our tasks will need a project + Project project = new Project(); + + // Add the logger + project.addBuildListener(getLogger()); + + // Instantiate the copy task + Copy copyTask = new Copy(); + + // Build the file set + FileSet fileset = new FileSet(); + fileset.setIncludes("*.xml"); + fileset.setIncludes("*.xsl"); + fileset.setDir(new File(".")); + + // Add the file set to the copy task + copyTask.addFileset(fileset); + // Set the destination for the files + copyTask.setTodir(new File("copydir")); + + // The name of this task + copyTask.setTaskName("copyer"); + + // Add the copy task to the project + copyTask.setProject(project); + + // Call init() as good practice + copyTask.init(); + + // Copy the files + copyTask.execute(); + } + + /** + *

Returns the default logger for the project.

+ * @return DefaultLogger + */ + private static DefaultLogger getLogger() { + // The logger for this class + DefaultLogger logger = new DefaultLogger(); + + // The default logger needs somewhere to write to + PrintStream out = System.out; + + // Set the output streams for the logger + logger.setOutputPrintStream(out); + logger.setErrorPrintStream(out); + + // Set the message threshold for this logger + logger.setMessageOutputLevel(Project.MSG_INFO); + + return logger; + } +} diff --git a/ch12/ant/org/mwrm/ant/api/Deployer.java b/ch12/ant/org/mwrm/ant/api/Deployer.java new file mode 100644 index 0000000..08e7c40 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/api/Deployer.java @@ -0,0 +1,354 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.api; + +import java.io.File; +import java.io.PrintStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DefaultLogger; + +import org.apache.tools.ant.listener.Log4jListener; + +import org.apache.catalina.ant.DeployTask; +import org.apache.catalina.ant.UndeployTask; + +/** + *

This class deploys or undeploys a web application from the Tomcat server, + * using the Ant <deploy> and <undeploy> + * tasks.

+ * + *

The following is this class's usage information:

+ * + * + * + * + * + * + * + * + * + *
Deployer action [options]
Action:
-a, -action <deploy filename.war [-path <path>] | + * undeploy -path <path>>
Options:
-url <url>
-u, -username <username>
-p, -password <password>
-l, -logfile <logfile>
-log4j
+ * + *

The deploy option uses the {@link DeployTask DeployTask} + * and the undeploy option + * uses the {@link UndeployTask UndeployTask}. + * If the user specifies the -log4j option, + * this class will log with the {@link Log4jListener Log4jListener}.

+ */ +public final class Deployer { + + /** + *

The default constructor.

+ */ + private Deployer() { } + + /** The default URL for the manager application. */ + private static String managerUrl = "localhost:8080/manager"; + + /** The path. We'll build the default path below */ + private static String path = ""; + + /** The username. */ + private static String username = ""; + + /** The password. */ + private static String password = ""; + + /** The filename of the WAR. */ + private static String filename; + + /** The user's desired action. */ + private static String action = ""; + + /** Sets whether the default logger + * will use the log file or System.out. */ + private static boolean useLogFile = false; + + /** The log file for the default logger. */ + private static String logFile; + + /** Sets whether we use the Log4j listener. */ + private static boolean useLog4j = false; + + /** + *

Deploys or undeploys a web application, + * depending on which command-line options + * the user specifies.

+ * @param args The command-line arguments + */ + public static void main(final String[] args) { + + // Process the arguments and set class members + processArgs(args); + + // A final set of checks + if (action.equals("undeploy") && path.equals("")) { + usage("You must specify a path when undeploying."); + } else if (action.equals("deploy") && filename == null) { + usage("You must specify a file when deploying."); + } else if (action.equals("")) { + usage("You must specify an action with -a or -action."); + } + + // Our tasks will need a project + Project project = new Project(); + + // Add the logger + project.addBuildListener(getLogger()); + + // Does the user want to use Log4j? + if (useLog4j) { + // The listener is configured with the log4j.properties file + Log4jListener listener = new Log4jListener(); + project.addBuildListener(listener); + } + + // The deployer that will deploy the WAR file + DeployTask deployer = new DeployTask(); + // The undeployer that will undeploy the application + UndeployTask undeployer = new UndeployTask(); + + // Check what we want to do + if (action.equals("deploy")) { + // The task needs the project's logger + deployer.setProject(project); + + // Call init() as good practice + deployer.init(); + + // The name of this task + deployer.setTaskName("deployer"); + + // The next few methods set the attributes of the task + deployer.setUsername(username); + deployer.setPassword(password); + deployer.setUrl("http://" + managerUrl); + deployer.setWar("file:" + filename); + deployer.setPath(path); + deployer.setUpdate(true); + + try { + // Run the task + deployer.execute(); + } catch (BuildException be) { + // The three ways to log with a task + //System.out.println(be.getMessage()); + //project.log(be.getMessage()); + if (!(be.getMessage().indexOf("FAIL") > -1)) { + deployer.log(be.getMessage()); + } + } + } else { + // The task needs the project's logger + undeployer.setProject(project); + + // Call init() as good practice + undeployer.init(); + + // The name of this task + undeployer.setTaskName("undeployer"); + + // The next few methods set the attributes of the task + undeployer.setUsername(username); + undeployer.setPassword(password); + undeployer.setUrl("http://" + managerUrl); + undeployer.setPath(path); + + try { + // Run the task + undeployer.execute(); + //System.out.println("Undeployed " + path + "."); + } catch (BuildException be) { + // The three ways to log with a task + //System.out.println(be.getMessage()); + //project.log(be.getMessage()); + if (!(be.getMessage().indexOf("FAIL") > -1)) { + undeployer.log(be.getMessage()); + } + } + } + } // end of main() + + /** + *

Processes the command-line arguments + * and sets member variables.

+ * @param args The command-line arguments + */ + private static void processArgs(final String[] args) { + try { + // We'll go through the command-line arguments + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + + // Check to see if the user has specified an action + if (arg.equals("-a") || arg.equals("-action")) { + // If it's "undeploy", we'll remember that + if (args[i + 1].equals("undeploy")) { + action = "undeploy"; + i++; + // If it's "deploy", we'll remember that + } else if (args[i + 1].equals("deploy")) { + action = "deploy"; + i++; + // If it's not "undeploy" or "deploy", it's incorrect + } else { + usage(); + } + // Check for the path + } else if (arg.equals("-path")) { + path = args[i + 1]; + i++; + // Check for the URL + } else if (arg.equals("-url")) { + managerUrl = args[i + 1]; + i++; + // Check for the username + } else if (arg.equals("-u") || arg.equals("-username")) { + username = args[i + 1]; + i++; + // Check for the password + } else if (arg.equals("-p") || arg.equals("-password")) { + password = args[i + 1]; + i++; + // Check if the user wants to use a log file + } else if (arg.equals("-l") || arg.equals("-logfile")) { + logFile = args[i + 1]; + useLogFile = true; + i++; + // Check if the user wants to use Log4j + } else if (arg.equals("-log4j")) { + useLog4j = true; + // If the user has specified any other argument, + // it's incorrect + } else if (arg.startsWith("-")) { + String msg = "Unknown argument: " + arg; + usage(msg); + // If there's no prefix it's our WAR file + // We only check for it if we're deploying + } else if (action.equals("deploy")) { + // This must be our WAR file + filename = arg; + + // Create a file object + File warFile = new File(filename); + + // Check if this file actually exists + if (!warFile.exists()) { + String msg = "File " + arg + " does not exist."; + System.out.println(msg); + System.exit(-1); + } + + // We should set the path if the user did not + // The path must begin with a '/' + if (path.equals("")) { + // If the WAR file is not in the current directory, + // there will be a slash + int begin = filename.lastIndexOf("/"); + // We'll add a slash or not + // depending on where the WAR is + String slash = ""; + + // If there is no slash, the index will be -1 + if (begin == -1) { + // Therefore, we need to take the whole file name + begin = 0; + // and add a slash to the path + slash = "/"; + } + // Build the path by removing the .war extension + path = slash + + filename + .substring(begin, filename.lastIndexOf(".war")); + } + } + } + // If a command-line options is not followed by another argument + // the checks above will throw a ArrayIndexOutOfBoundsException + } catch (ArrayIndexOutOfBoundsException aioobe) { + usage(); + } + } + + /** + *

Returns the default logger for the project.

+ * @return DefaultLogger + */ + private static DefaultLogger getLogger() { + // The logger for this class + DefaultLogger logger = new DefaultLogger(); + + // The default logger needs somewhere to write to + PrintStream out = null; + + // Does the user want to write to a file? + if (useLogFile) { + try { + // We'll log to the file the user specified + out = new PrintStream(new FileOutputStream(logFile, true)); + } catch (FileNotFoundException fnfe) { + // We can't use the log just yet + System.out.println(fnfe.getMessage()); + // We'll fall back to System.out + System.out.println("Using the console."); + out = System.out; + } + } else { + // The default is to print to System.out + out = System.out; + } + + // Set the output streams for the logger + logger.setOutputPrintStream(out); + logger.setErrorPrintStream(out); + + // Set the message threshold for this logger + logger.setMessageOutputLevel(Project.MSG_INFO); + + return logger; + } + + /**

Displays the usage information.

*/ + private static void usage() { + System.out.println("Usage information:"); + System.out.println("Deployer action [options]"); + System.out.println("Action:"); + String actionMsg = "-a, -action ] " + + "| undeploy -path >"; + System.out.println(actionMsg); + System.out.println("Options:"); + System.out.println("-url "); + System.out.println("-u, -username "); + System.out.println("-p, -password "); + System.out.println("-l, -logfile "); + System.out.println("-log4j"); + + System.exit(-1); + } + + /**

Displays a custom message, then usage information.

+ * @param message The message to print + */ + private static void usage(final String message) { + System.out.println(message); + usage(); + } +} diff --git a/ch12/ant/org/mwrm/ant/api/package.html b/ch12/ant/org/mwrm/ant/api/package.html new file mode 100644 index 0000000..9943883 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/api/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.api Package + + +

A package that contains classes that demonstrate the Ant API.

+ + diff --git a/ch12/ant/org/mwrm/ant/listeners/BuildEventListener.java b/ch12/ant/org/mwrm/ant/listeners/BuildEventListener.java new file mode 100644 index 0000000..15f3dd1 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/listeners/BuildEventListener.java @@ -0,0 +1,116 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.listeners; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildListener; +import org.apache.tools.ant.BuildEvent; + +/** + *

A class that demonstrates some of the functionality + * of a custom listener.

+ */ +public class BuildEventListener implements BuildListener { + + /** + *

Signals that a build has started. This event + * is fired before any targets have started.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + */ + public final void buildStarted(final BuildEvent start) { + start.getProject().log("buildStarted() called.", Project.MSG_ERR); + } + + /** + *

Signals that the last target has finished. This event + * will still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void buildFinished(final BuildEvent finish) { + finish.getProject().log("buildFinished() called.", Project.MSG_ERR); + } + + /** + *

Signals that a target is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTarget() + */ + public final void targetStarted(final BuildEvent start) { + start.getProject().log("Target [" + start.getTarget().getName() + + "] started.", Project.MSG_ERR); + } + + /** + *

Signals that a target has finished. This event will + * still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void targetFinished(final BuildEvent finish) { + finish.getProject().log("Target [" + finish.getTarget().getName() + + "] finished.", Project.MSG_ERR); + } + + /** + *

Signals that a task is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTask() + */ + public final void taskStarted(final BuildEvent start) { + start.getProject().log("Task [" + start.getTask().getTaskName() + + "] started.", Project.MSG_ERR); + } + + /** + *

Signals that a task has finished. This event will still + * be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public final void taskFinished(final BuildEvent finish) { + finish.getProject().log("Task [" + finish.getTask().getTaskName() + + "] finished.", Project.MSG_ERR); + } + + /**

When a message is sent to this logger, Ant calls this method.

+ * @param event An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public void messageLogged(final BuildEvent event) { + // empty + } +} diff --git a/ch12/ant/org/mwrm/ant/listeners/package.html b/ch12/ant/org/mwrm/ant/listeners/package.html new file mode 100644 index 0000000..c12041d --- /dev/null +++ b/ch12/ant/org/mwrm/ant/listeners/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.listeners Package + + +

A package that contains custom listeners.

+ + diff --git a/ch12/ant/org/mwrm/ant/loggers/BuildEventLogger.java b/ch12/ant/org/mwrm/ant/loggers/BuildEventLogger.java new file mode 100644 index 0000000..60eb52c --- /dev/null +++ b/ch12/ant/org/mwrm/ant/loggers/BuildEventLogger.java @@ -0,0 +1,195 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.loggers; + +import java.io.PrintStream; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildLogger; +import org.apache.tools.ant.BuildEvent; + +/** + *

A class that demonstrates some of the functionality + * of a custom logger.

+ */ +public class BuildEventLogger implements BuildLogger { + + /** + * PrintStream to write non-error messages to. + */ + private PrintStream out; + + /** + * PrintStream to write error messages to. + */ + private PrintStream err; + + /** + * Sets whether to tailor output for Emacs, etc. + * The default is false. + */ + private boolean emacsMode = false; + + /** + * We'll set this logger to log only warnings. + */ + private int msgOutputLevel = Project.MSG_WARN; + + /** + *

Signals that a build has started. This event + * is fired before any targets have started.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + */ + public final void buildStarted(final BuildEvent start) { + start.getProject().log("Message from buildStarted().", Project.MSG_ERR); + } + + /** + *

Signals that the last target has finished. This event + * will still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void buildFinished(final BuildEvent finish) { + // empty + } + + /** + *

Signals that a target is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTarget() + */ + public void targetStarted(final BuildEvent start) { + // empty + } + + /** + *

Signals that a target has finished. This event will + * still be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void targetFinished(final BuildEvent finish) { + // empty + } + + /** + *

Signals that a task is starting.

+ * + * @param start An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getTask() + */ + public void taskStarted(final BuildEvent start) { + // empty + } + + /** + *

Signals that a task has finished. This event will still + * be fired if an error occurred during the build.

+ * + * @param finish An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getException() + */ + public void taskFinished(final BuildEvent finish) { + // empty + } + + /**

When a message is sent to this logger, Ant calls this method.

+ * @param event An event with any relevant extra information. + * Must not be null. + * + * @see BuildEvent#getMessage() + * @see BuildEvent#getPriority() + */ + public final void messageLogged(final BuildEvent event) { + /// We need to determine how important this message is + int priority = event.getPriority(); + + // If it's as important as our log level, we display it + if (priority <= msgOutputLevel) { + out.println("messageLogged: " + event.getMessage()); + } + } + + /** + *

Sets the output stream to which this logger is to send its output.

+ * + * @param output The output stream for the logger. + * Must not be null. + */ + public final void setOutputPrintStream(final PrintStream output) { + this.out = new PrintStream(output, true); + } + + /** + *

Sets the output stream to which this logger + * is to send error messages.

+ * + * @param errorStream The error stream for the logger. + * Must not be null. + */ + public final void setErrorPrintStream(final PrintStream errorStream) { + this.err = new PrintStream(errorStream, true); + } + + /** + *

Sets this logger to produce Emacs + * (and other editor) friendly output.

+ * + * @param mode true if output is to be unadorned so that + * Emacs and other editors can parse files names, etc. + */ + public final void setEmacsMode(final boolean mode) { + this.emacsMode = mode; + } + + /** + *

Sets the highest level of message this logger should respond to.

+ * + *

Only messages with a message level lower than or equal to the + * given level should be written to the log.

+ *

+ * Constants for the message levels are in the + * {@link Project Project} class. The order of the levels, from least + * to most verbose, is MSG_ERR, MSG_WARN, + * MSG_INFO, MSG_VERBOSE, + * MSG_DEBUG.

+ *

The default for this logger is + * {@link Project#MSG_WARN Project.MSG_WARN}.

+ * + * @param level the logging level for the logger. + */ + public void setMessageOutputLevel(final int level) { + // We will leave this empty to use the default level, + // which we set above + } + +} diff --git a/ch12/ant/org/mwrm/ant/loggers/package.html b/ch12/ant/org/mwrm/ant/loggers/package.html new file mode 100644 index 0000000..e30bac9 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/loggers/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.loggers Package + + +

A package that contains custom loggers.

+ + diff --git a/ch12/ant/org/mwrm/ant/tasks/ClassSetTask.java b/ch12/ant/org/mwrm/ant/tasks/ClassSetTask.java new file mode 100644 index 0000000..5afbdfd --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/ClassSetTask.java @@ -0,0 +1,60 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

The ClassSetTask class + * demonstrates how to use a Class argument + * in a custom class attribute.

+ * + */ + +public class ClassSetTask extends Task { + + /** The qualified class name. */ + private Class qualifiedName; + + /** + *

Runs the task and displays the qualified name of the class + * that is set as the setQualifiedName attribute.

+ * + */ + public final void execute() { + log("qualifiedName: " + qualifiedName, Project.MSG_INFO); + } + + /** + *

Sets the fully qualified name of the class.

+ ** @param qName The fully qualified name of a class + */ + public final void setQualifiedName(final Class qName) { + if (qName.getName().equals("java.lang.Integer") + || + qName.getName().equals("java.lang.String")) { + log(qName.getName() + " found.", Project.MSG_INFO); + } else { + String msg = "You can only specify java.lang.Integer " + + "or java.lang.String in qualifiedName."; + throw new BuildException(msg); + } + this.qualifiedName = qName; + } +} + diff --git a/ch12/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java b/ch12/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java new file mode 100644 index 0000000..e9c076c --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/ExtendJavadocTask.java @@ -0,0 +1,188 @@ +/* + * Extends org.apache.tools.ant.taskdefs.Javadoc + * and uses org.apache.tools.ant.taskdefs.UpToDate, + * which are Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import java.io.File; + +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; + +import org.apache.tools.ant.taskdefs.Javadoc; +import org.apache.tools.ant.taskdefs.UpToDate; + +import org.apache.tools.ant.types.FileSet; + +/** + *

The ExtendJavadocTask class + * extends the {@link org.apache.tools.ant.taskdefs.Javadoc Javadoc} task. + * It checks whether a set of source files are newer than a set of target files + * and if so, it generates Javadocs.

+ * + */ + +public class ExtendJavadocTask extends Javadoc { + + /** The attribute of the task element. */ + private File target; + + /** A set of file sets, + * each of which is provided by a nested file set. */ + private Vector targetFileSets = new Vector(); + + /** The internal uptodate task. */ + private UpToDate utd; + + /** + *

Creates a new instance of an internal + * <uptodate> task + * and adds it to the current project.

+ * @see UpToDate org.apache.tools.ant.taskdefs.UpToDate + */ + public final void init() { + // We need an instance of the task + utd = new UpToDate(); + // We need to add the task to this project + utd.setProject(this.getProject()); + } + + /** + *

Checks if Javadocs should be created + * and then calls super.execute() if so.

+ *

This method does usage checks on the task's attributes + * and its nested elements. + * It will throw a BuildException if there is a violation.

+ */ + public final void execute() { + // This is the usage information + + // We can't have a target attribute + // and nested targetfiles elements + if (target != null && targetFileSets.size() > 0) { + String msg = "You can't specify a targetfile attribute " + + "and elements."; + throw new BuildException(msg); + } + // We have to specify either a target attribute + // or at least one nested targetfiles elements + if (target == null && targetFileSets.size() == 0) { + String msg = "You must specify either a targetfile attribute " + + "or at least one element."; + throw new BuildException(msg); + } + + // If this is false, the files aren't up to date + // and we have to run the javadocs + boolean eval = false; + + // If we've got to this point, we know the usage must be correct. + // Let's check whether a single target attribute has been used. + + if (target != null) { + // Get the results of the check + eval = getResult(target); + } else { + // If a target attribute wasn't specified, + // at least one nested targetfiles element was. + + // We first get all the file sets represented by the nested elements + Enumeration e = targetFileSets.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + + // The next element is a file set, so we get its files + // in a semi-colon-separated list + String files = e.nextElement().toString(); + + // Next, we split the list into its file names + StringTokenizer st = new StringTokenizer(files, ";"); + + // And iterate over them to test each one + while (st.hasMoreTokens()) { + // We create a file + //from the current file name in the iteration + File tempTarget = new File(st.nextToken()); + + // Get the results of the check + eval = getResult(tempTarget); + + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + // One false result is enough to fail the whole file set + if (!eval) { + break; + } + } + } + + // If the test failed, we want to generate Javadocs + if (!eval) { + super.execute(); + } else { + log("Skipping Javadoc creation. The files are up to date.", + Project.MSG_INFO); + } + } + + /**

Checks whether the files are up to date.

+ * @param file The file to evaluate + * @return boolean The result + */ + private boolean getResult(final File file) { + // Set the target property in the task + utd.setTargetFile(file); + // Evaluate the files + return utd.eval(); + } + + /** + *

The setter method for the target attribute.

+ * @param targetFile A file to check against + */ + public final void setTarget(final File targetFile) { + this.target = targetFile; + } + + /** + *

The setter method for the file set + * contained in the nested <srcfiles> element.

+ * @param fileset A file set of source files + */ + public final void addSrcfiles(final FileSet fileset) { + utd.addSrcfiles(fileset); + } + + /** + *

The setter method for the file sets + * contained in nested <targetfiles> elements.

+ * @param fileset A file set of target files + */ + public final void addTargetfiles(final FileSet fileset) { + targetFileSets.add(fileset); + } +} + diff --git a/ch12/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java b/ch12/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java new file mode 100644 index 0000000..bfd1215 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/LifeCycleNestedTask.java @@ -0,0 +1,347 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Vector; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ * + *

This class demonstrates how to nest elements within a custom task. + * Its nested element is called <name> + * and cannot be used in conjunction with a name attribute.

+ */ +public class LifeCycleNestedTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** The collection of name elements. */ + private Vector nameElements = new Vector(); + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleNestedTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + this.name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name != null && nameElements.size() > 0) { + String msg = "You can't specify a name attribute " + + "and elements."; + throw new BuildException(msg); + } + if (name == null && nameElements.size() == 0) { + String msg = "You must specify either a name attribute " + + "or at least one element."; + throw new BuildException(msg); + } + if (nameElements.size() > 0 && text != null) { + String msg = "You can't specify elements " + + "and body text."; + throw new BuildException(msg); + } + + logAll("execute()"); + + // If name is not set, we want to check nested elements + if (name == null) { + // Get the name elements + Enumeration e = nameElements.elements(); + + // And then iterate over them + while (e.hasMoreElements()) { + NameElement nameElement = (NameElement) e.nextElement(); + + // Usage check + if (nameElement.getName() == null) { + String msg = "You must specify a name attribute " + + "or body text for a nested element."; + throw new BuildException(msg); + } + log("Value of name element: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + log("---------------", Project.MSG_VERBOSE); + } + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see if this object is a custom task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + Task aTask = (Task) obj; + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + + // If name is set, we only have one value to print + if (name != null) { + log("Value of name attribute: " + this.name, Project.MSG_VERBOSE); + } + + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + + " at line " + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + // + " and column " + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addName()"); + log("Value of this name: " + + nameElement.getName(), Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has been initialized.

+ * @param nameElement The NameElement + * object that represents the nested element + * @see LifeCycleNestedTask.NameElement + */ + public final void addConfiguredName(final NameElement nameElement) { + nameElements.add(nameElement); + + logAll("addConfiguredName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + } + + /** + *

Adds a <name> element + * that has not been initialized. + * In this case, the createName() method + * has the responsibility + * for creating the object.

+ * @see LifeCycleNestedTask.NameElement + */ + public final NameElement createName() { + NameElement nameElement = new NameElement("Madeleine"); + + nameElements.add(nameElement); + + logAll("createName()"); + log("Value of this name: " + nameElement.getName(), + Project.MSG_VERBOSE); + + return nameElement; + } + + /** + *

A class that implements + * the nested <name> element + * of a LifeCycleNestedTask. + * @see LifeCycleNestedTask + */ + public static class NameElement { + + /** The name attribute of this element. */ + private String name; + + /** Tells the class if we've used the overridden constructor. */ + private boolean usedConstructor = false; + + /** The empty constructor. */ + public NameElement() { + // Empty + } + + /** + *

Used by the LifeCycleNestedTask.createName() method + * to created a nested <name> element.

+ * @see LifeCycleNestedTask#createName() + */ + public NameElement(final String text) { + this.name = text; + usedConstructor = true; + } + + /** + *

The mutator method for the name attribute.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + this.name = aName; + } + + /** + *

The accessor method for the name attribute.

+ * @return String The name to display + */ + public final String getName() { + return name; + } + + /** + *

Sets the body text of the <name> element. + * It contains a usage check.

+ * @param text The body text + */ + public final void addText(final String text) { + // Usage check + if (name != null && !usedConstructor) { + String msg = "You can't specify a name attribute " + + "and nested text in elements."; + throw new BuildException(msg); + } else { + this.name = text.trim(); + } + } + } +} + diff --git a/ch12/ant/org/mwrm/ant/tasks/LifeCycleTask.java b/ch12/ant/org/mwrm/ant/tasks/LifeCycleTask.java new file mode 100644 index 0000000..51c5219 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/LifeCycleTask.java @@ -0,0 +1,194 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.mwrm.ant.tasks; + +import java.util.Hashtable; +import java.util.Enumeration; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +/** + *

At each stage in a task's life cycle, this class displays information + * to show the internal state of the task and its position with in the project. + * It takes a name attribute.

+ */ +public class LifeCycleTask extends Task { + + /** The name attribute of this task. */ + private String name; + + /** The body text of this task. */ + private String text; + + /** + *

The constructor displays the state of the task + * as it is instantiated.

+ */ + public LifeCycleTask() { + System.out.println("---------------"); + System.out.println("Constructor called"); + System.out.println("Value of name attribute: " + name); + System.out.println("Value of the body text: " + text); + System.out.println("Project: " + getProject()); + System.out.println("Location: " + getLocation()); + System.out.println("Target: " + getOwningTarget()); + System.out.println("---------------"); + } + + /** + *

Displays the state of the task at initialization.

+ * @see #logAll(String method) + */ + public final void init() { + logAll("init()"); + } + + /** + *

Displays the state of the task when Ant runs it. + * This method also runs some usage checks + * to ensure the task is being used properly.

+ */ + public final void execute() { + if (name == null) { + throw new BuildException("You must specify a name attribute in " + + getTaskName() + "."); + } + logAll("execute()"); + + // Write the name to output + log(name, Project.MSG_INFO); + } + + /** + *

Sets the name to display + * and shows the state of the task afterwards.

+ * @param aName The name to display + */ + public final void setName(final String aName) { + // The value of the name attribute + this.name = aName; + logAll("setName()"); + } + + /** + *

Sets the body text of the task + * and shows the state of the task afterwards.

+ * @param bodyText The body text + */ + public final void addText(final String bodyText) { + // If the body text is just whitespace, it might as well be null + if (bodyText.trim().equals("")) { + this.text = null; + } else { + this.text = bodyText.trim(); + } + logAll("addText()"); + } + + /**

Checks for task references.

+ * @return String + * A string that tells us details of the reference check + */ + private String referenceCheck() { + + // The default setting + String setString = "Reference not found."; + + // We need the references that have been set in this project + Hashtable refs = getProject().getReferences(); + Enumeration e = refs.elements(); + + // Let's iterate over them + while (e.hasMoreElements()) { + // We want to work with each object, so we'll instantiate an object + Object obj = e.nextElement(); + + // Check to see whether this object is a task + // If it is, we'll build a string that contains its name and type + if (obj.getClass().getName(). + equals("org.apache.tools.ant.UnknownElement") + || + obj.getClass().getName(). + equals(this.getClass().getName())) { + + Task aTask = (Task) obj; + + setString = + "Reference to " + aTask.getTaskName() + " found, of type " + + aTask.getClass().getName() + ". "; + setString = setString + "Its id is " + + aTask.getRuntimeConfigurableWrapper(). + getAttributeMap().get("id") + "."; + } + } + return setString; + } + + /** + *

A central logging method that all the life-cycle methods call + * to display the state of the task. + * It displays the value of the name attribute + * and other information about the task, + * including the name of its project and its location in the build file.

+ * @param method The name of the method that issued the logging call + */ + public final void logAll(final String method) { + log("---------------", Project.MSG_VERBOSE); + log(method + " called", Project.MSG_VERBOSE); + log("Value of name attribute: " + name, Project.MSG_VERBOSE); + log("Value of the body text: " + text, Project.MSG_VERBOSE); + log("Project: " + getProject().getName(), Project.MSG_VERBOSE); + + // Here we build some information on the location + // within the build file + String locationString = getLocation().getFileName(); + locationString = locationString + " at line " + + getLocation().getLineNumber(); + + // Location.getColumnNumber() is for Ant 1.7+ + // Comment it out if you are using Ant 1.6.x + //locationString = locationString + " and column " + // + getLocation().getColumnNumber(); + + log("Location: " + locationString, Project.MSG_VERBOSE); + + // We could use the Location.toString() method + //log("Location: " + getLocation(), Project.MSG_VERBOSE); + + log("Target: " + getOwningTarget(), Project.MSG_VERBOSE); + + // referenceCheck() returns a string with information + // on any references to custom tasks + log(referenceCheck(), Project.MSG_VERBOSE); + + // If the configuration wrapper is null, we use its + // run-time equivalent + if (getWrapper() == null) { + log("Reference id: " + + getRuntimeConfigurableWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } else { + // This time we use the protected getWrapper() method + log("Reference id: " + getWrapper().getAttributeMap().get("id"), + Project.MSG_VERBOSE); + } + + log("---------------", Project.MSG_VERBOSE); + } +} + diff --git a/ch12/ant/org/mwrm/ant/tasks/ProjectHelpTask.java b/ch12/ant/org/mwrm/ant/tasks/ProjectHelpTask.java new file mode 100644 index 0000000..32172b0 --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/ProjectHelpTask.java @@ -0,0 +1,72 @@ +/* + * Uses org.apache.tools.ant.Main, + * which is Copyright 2000-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.ant.tasks; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Main; + +/** + *

The ProjectHelpTask class displays usage information + * for the current project. This is the same information as is displayed + * by -projecthelp.

+ * + */ + +public class ProjectHelpTask extends Task { + + /** The location of the build file to use + * when obtaining usage information. */ + private String buildfile; + + /** + *

Runs the task. + * It calls {@link org.apache.tools.ant.Main#main(String[] args) + * org.apache.tools.ant.Main.main()} with the -projecthelp + * parameter. It will also send the current build file's file name + * via the -f parameter.

+ * + *

The buildfile attribute is optional. + * The default is the task's build file.

+ * @see org.apache.tools.ant.Main org.apache.tools.ant.Main + */ + public final void execute() { + // If the buildfile attribute is null, we'll use the task's build file + if (buildfile == null) { + buildfile = getLocation().getFileName(); + } + + // The arguments that we will pass to the Main class. + // The buildfile attribute must follow the -f parameter. + String[] args = {"-projecthelp", "-f", buildfile}; + + // Call the Main Ant class with the arguments. + Main.main(args); + } + + /** + * Setter method for the buildfile attribute. + * @param file The file name of the build file to use. + * + */ + public final void setBuildfile(final String file) { + this.buildfile = file; + } + +} + diff --git a/ch12/ant/org/mwrm/ant/tasks/package.html b/ch12/ant/org/mwrm/ant/tasks/package.html new file mode 100644 index 0000000..26003dc --- /dev/null +++ b/ch12/ant/org/mwrm/ant/tasks/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.ant.tasks Package + + +

A collection of classes that demonstrate the Ant task life cycle.

+ + diff --git a/ch12/antBook.antlib.xml b/ch12/antBook.antlib.xml new file mode 100644 index 0000000..7927073 --- /dev/null +++ b/ch12/antBook.antlib.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch12/antlib.xml b/ch12/antlib.xml new file mode 100644 index 0000000..72d6f9a --- /dev/null +++ b/ch12/antlib.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch12/build.properties b/ch12/build.properties new file mode 100644 index 0000000..e1a014e --- /dev/null +++ b/ch12/build.properties @@ -0,0 +1,144 @@ +# The source directory that contains the code +src=src + +# Subdirectory properties +src.shared.root=${src}/shared +src.shared.java=${src.shared.root}/java +src.shared.docs=${src.shared.root}/docs +src.shared.conf=${src.shared.root}/conf + +src.stand-alone.root=${src}/stand-alone +src.stand-alone.java=${src.stand-alone.root}/java +src.stand-alone.docs=${src.stand-alone.root}/docs + +src.web.root=${src}/web +src.web.java=${src.web.root}/java +src.web.docs=${src.web.root}/docs +src.web.pages=${src.web.root}/pages +src.web.tags=${src.web.root}/tags +src.web.conf=${src.web.root}/conf + +# The scratch directory +build=build + +build.stand-alone.root=${build}/stand-alone + +build.web.root=${build}/web +build.web.web-inf=${build.web.root}/WEB-INF +build.web.classes=${build.web.web-inf}/classes +build.web.tags=${build.web.web-inf}/tags +build.web.lib=${build.web.web-inf}/lib + +# The final destination of our project files +dist=dist + +# The location of third-party JAR files +lib=lib + +# This name will be appended to the JAR and WAR files +appName=antBook +appName.jar=${dist}/${appName}.jar +appName.war=${dist}/${appName}.war + +# The Tomcat home directory +catalina.home=C:\\jakarta-tomcat-5.5.9 +servlet24.jar=${catalina.home}/common/lib/servlet-api.jar +# Use the following line if using Ant to download the JAR +#servlet24.jar=${lib}/servlet-api.jar + +# Required for the JSTL build +jsp20.jar=${catalina.home}/common/lib/jsp-api.jar +# Use the following line if using Ant to download the JAR +#jsp20.jar=${lib}/jsp-api.jar +jstl.build=jakarta-taglibs/standard +library.src=src +examples.src=examples +doc.src=doc +build.library=${build} + +# Required for the MySQL connector build +mysql.build=mm.mysql-2 +mysql.name=mysql-connector +mysql.jar=${lib}/${mysql.name}-bin.jar + +# The database properties file +database.properties=${src.shared.conf}/database.properties + +# The directory where the docs will go +docs=${build}/docs + +# Properties for customizing the Javadoc build +javadoc.doctitle=Welcome to the example application +javadoc.windowtitle=The example application +javadoc.j2se.version=1.5.0 +javadoc.j2ee.version=1.4 + +javadoc.j2se.offline=C:/j2sdk1.5.0/docs/api +javadoc.j2ee.offline=C:/j2eesdk1.4/docs/apidocs + +package.stand-alone=stand-alone +package.web=web +package.docs=docs + +tmp=C:/TEMP/antBook +#tmp=/tmp/antBook + +# FTP settings +ftp.server=localhost +ftp.src.dir=src +ftp.bin.dir=bin + +mail.from=antBuild@example.com +mail.tolist=antUser01@example.com,antUser02@example.com +mail.message.docs=Here's the docs distribution +mail.mailhost=smtp.mail.example.com +mail.subject=Ant build + +# The location of the Tomcat server +tomcat.host=localhost +tomcat.port=8080 +manager.url=http://${tomcat.host}:${tomcat.port}/manager + +database.root=jdbc:mysql://localhost:3306/ +driver.name=com.mysql.jdbc.Driver + +drop.sql=SQL/drop.sql +create.sql=SQL/create.sql +insert.sql=SQL/insert.sql + +# The name of the Tomcat start script +tomcat.executableName=startup + +# The test directory +test.src=test +test.build=${build}/test +test.junit.reports=${test.build}/reports/junit +test.junit.data=${test.build}/data/junit +test.junit.style=${test.src}/style/junit + +junit.home=C:/junit3.8.1 +junit.jar=${junit.home}/junit.jar + +httpunit.home=C:/httpunit +httpunit.jar=${httpunit.home}/lib/httpunit.jar + +last.test.failed.file=failed.txt + +checkstyle.home=C:/checkstyle +checkstyle.jar=${checkstyle.home}/checkstyle-3.5.jar +test.checkstyle.reports=${test.build}/reports/checkstyle +test.checkstyle.data=${test.build}/data/checkstyle + +# Custom task properties +ant.tasks.src=ant +ant.tasks.build=${build}/ant +ant.tasks.jar=${dist}/${appName}-tasks.jar + +ant.tasks.antlib.xml=antlib.xml +ant.tasks.antlib.package=org/mwrm/ant +ant.tasks.antlib.dir=${ant.tasks.build}/${ant.tasks.antlib.package} + +ant.api.jar=${dist}/${appName}-api.jar + +log4j.home=C:/logging-log4j-1.2.12 +log4j.jar=${log4j.home}/dist/lib/log4j.jar \ No newline at end of file diff --git a/ch12/build.xml b/ch12/build.xml new file mode 100644 index 0000000..cb2a594 --- /dev/null +++ b/ch12/build.xml @@ -0,0 +1,1459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Jones + + + + + + + + + + + + + + ${javadoc.doctitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${converted} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ch12/log.xml b/ch12/log.xml new file mode 100644 index 0000000..7c28a52 --- /dev/null +++ b/ch12/log.xml @@ -0,0 +1,14 @@ +java.lang.NullPointerException + at org.mwrm.ant.listeners.BuildEventListener.messageLogged(Unknown Source) + at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2009) + at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2029) + at org.apache.tools.ant.Project.log(Project.java:357) + at org.apache.tools.ant.Project.addReference(Project.java:1777) + at org.apache.tools.ant.PropertyHelper.getPropertyHelper(PropertyHelper.java:131) + at org.apache.tools.ant.Project.getProperty(Project.java:473) + at org.apache.tools.ant.XmlLogger.buildFinished(XmlLogger.java:166) + at org.apache.tools.ant.Project.fireBuildFinished(Project.java:1848) + at org.apache.tools.ant.Main.runBuild(Main.java:688) + at org.apache.tools.ant.Main.startAnt(Main.java:187) + at org.apache.tools.ant.launch.Launcher.run(Launcher.java:249) + at org.apache.tools.ant.launch.Launcher.main(Launcher.java:70) diff --git a/ch12/log.xsl b/ch12/log.xsl new file mode 100644 index 0000000..2272790 --- /dev/null +++ b/ch12/log.xsl @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + +
+ + http://jakarta.apache.org + + + Apache Ant +
+ + + +

+ + + + + + + +

+
+ Copyright © 2000-2002, Apache Software Foundation +
+
+ + +
+ + + + + + failed + complete + + + + + + + + + + + + + +
Build FailedBuild CompleteTotal Time:
+ +
+ See the stacktrace. +
+
+ + + + + +
ant.file
ant.version
java.version
os.name
+ +

Build events

+ + + + + + + +
targettaskmessage
+

+ + + +

Error details

+ + +
+
+
+ +

+
+ + + + + + + a + b + + + [ ] + + + + + + +
diff --git a/ch12/log4j.properties b/ch12/log4j.properties new file mode 100644 index 0000000..7fae6cf --- /dev/null +++ b/ch12/log4j.properties @@ -0,0 +1,94 @@ +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to a file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log + +# Use the simple layout +#log4j.appender.AntLogger.layout=org.apache.log4j.SimpleLayout + +#---------------------------------------------# +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to a pattern file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.pattern.log + +# Use a pattern layout +#log4j.appender.AntLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntLogger.layout.ConversionPattern=%d{ISO8601} : %p : %m %n + +#---------------------------------------------# +# Set the root logger for Ant +#log4j.rootLogger=INFO, AntLogger + +# Log to an HTML file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log.html + +# Set the layout to HTML and specify a title +#log4j.appender.AntLogger.layout=org.apache.log4j.HTMLLayout +#log4j.appender.AntLogger.layout.Title=Apress Ant Log + +#---------------------------------------------# +# Set the root logger for Ant +log4j.rootLogger=INFO, AntLogger + +# Log to the console +log4j.appender.AntLogger=org.apache.log4j.ConsoleAppender +log4j.appender.AntLogger.Target=System.out + +# Set a custom layout level +log4j.appender.AntLogger.layout=org.apache.log4j.PatternLayout +log4j.appender.AntLogger.layout.ConversionPattern=%d{ISO8601} : %p : %m %n + +#---------------------------------------------# +# Send all INFO messages and above to a file and +# all ERROR messages and above to the console +#log4j.rootLogger=INFO, AntINFO, AntERROR + +# Use a pattern file for the INFO messages +#log4j.appender.AntINFO=org.apache.log4j.FileAppender +#log4j.appender.AntINFO.File=C:/TEMP/antBook/logs/ant.pattern.log +#log4j.appender.AntINFO.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntINFO.layout.ConversionPattern=%d{ISO8601} : %r : %p : %m %n + +# Use an HTML file for ERROR messages +#log4j.appender.AntERROR=org.apache.log4j.FileAppender +#log4j.appender.AntERROR.File=C:/TEMP/antBook/logs/ant.log.html +#log4j.appender.AntERROR.layout=org.apache.log4j.HTMLLayout +#log4j.appender.AntERROR.layout.Title=Apress Error Log +#log4j.appender.AntERROR.Threshold=ERROR + +#---------------------------------------------# +# Use individual loggers for different components +#log4j.rootLogger=ERROR, AntLogger + +# Log to a file +#log4j.appender.AntLogger=org.apache.log4j.FileAppender +#log4j.appender.AntLogger.File=C:/TEMP/antBook/logs/ant.log + +# Use the simple layout +#log4j.appender.AntLogger.layout=org.apache.log4j.SimpleLayout + +# Set a logger for project components +#log4j.logger.org.apache.tools.ant.Project=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.Target=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.UnknownElement=INFO,AntComponentLogger +#log4j.logger.org.apache.tools.ant.taskdefs=INFO,AntComponentLogger + +#log4j.appender.AntComponentLogger=org.apache.log4j.FileAppender +#log4j.appender.AntComponentLogger.File=C:/TEMP/antBook/logs/ant.pattern.log +#log4j.appender.AntComponentLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntComponentLogger.layout.ConversionPattern=%p: %m: %d{ISO8601} %n +#log4j.appender.AntComponentLogger.Threshold=INFO + +# Log to a file +#log4j.appender.AntComponentLogger=org.apache.log4j.FileAppender +#log4j.appender.AntComponentLogger.File=C:/TEMP/antBook/logs/ant.component.log + +# Use the pattern layout +#log4j.appender.AntComponentLogger.layout=org.apache.log4j.PatternLayout +#log4j.appender.AntComponentLogger.layout.ConversionPattern=%c: %m %n \ No newline at end of file diff --git a/ch12/mail.log.properties b/ch12/mail.log.properties new file mode 100644 index 0000000..badfd74 --- /dev/null +++ b/ch12/mail.log.properties @@ -0,0 +1,8 @@ +MailLogger.mailhost=smtp.mail.yahoo.co.uk +MailLogger.user=antBook +MailLogger.password=antB00k +MailLogger.from=ant.log@example.com +MailLogger.failure.to=ant.results@example.com +MailLogger.success.to=ant.results@example.com +MailLogger.failure.subject=Build failed +MailLogger.success.subject=Build succeeded diff --git a/ch12/src/shared/conf/SQL/plants.sql b/ch12/src/shared/conf/SQL/plants.sql new file mode 100644 index 0000000..f95cb20 --- /dev/null +++ b/ch12/src/shared/conf/SQL/plants.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS plants; + +USE plants; + +CREATE TABLE plants ( + id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + common_name VARCHAR(80), + family VARCHAR(50) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(20), + cultivar_name VARCHAR(80) +); \ No newline at end of file diff --git a/ch12/src/shared/conf/SQL/plantsDelete.sql b/ch12/src/shared/conf/SQL/plantsDelete.sql new file mode 100644 index 0000000..ea4dd57 --- /dev/null +++ b/ch12/src/shared/conf/SQL/plantsDelete.sql @@ -0,0 +1,3 @@ +USE plants; + +DELETE FROM plants; \ No newline at end of file diff --git a/ch12/src/shared/conf/SQL/plantsDrop.sql b/ch12/src/shared/conf/SQL/plantsDrop.sql new file mode 100644 index 0000000..999053f --- /dev/null +++ b/ch12/src/shared/conf/SQL/plantsDrop.sql @@ -0,0 +1 @@ +DROP DATABASE IF EXISTS plants; \ No newline at end of file diff --git a/ch12/src/shared/conf/SQL/plantsInsert.sql b/ch12/src/shared/conf/SQL/plantsInsert.sql new file mode 100644 index 0000000..1b11952 --- /dev/null +++ b/ch12/src/shared/conf/SQL/plantsInsert.sql @@ -0,0 +1,15 @@ +USE plants; + +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Galanthus nivalis", "Snowdrop", "Liliaceae", "Snowdrops appear in late winter and early spring.", "gal_niv.jpg"); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus pseudonarcissus", "Daffodil, Lent lily", "Liliaceae", "Daffodils appear in spring."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Narcissus poeticus var. recurvus", "Old Pheasant's Eye, Poet's narcissus", "Liliaceae", "A very late-flowering daffodil with white petals and a short orange/yellow trumpet."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Leucojum vernum", "Spring snowflake", "Liliaceae", "A lovely spring bulb that flowers in spring. It resembles a robust snowdrop."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Iris pseudacorus", "Yellow iris, Yellow flag", "Iridaceae", "Commonly found by water and damp places. It spreads quickly and can easily take over an area once introduced."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Crocus tommasinianus", "Early crocus", "Iridaceae", "An early flowering crocus with gentle lavender-coloured flowers."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Colchicum autumnale", "Meadow saffron, Autumn crocus", "Liliaceae", "Flowers in August and September months after its leaves have died away. Resembles a crocus very closely."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Hyacinthoides non-scripta", "Bluebell", "Liliaceae", "Cloaks the floor of deciduous woodland throughout Europe."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Erythronium dens-canis", "Dog's tooth violet", "Liliaceae", "The bulb is shaped like a dog's tooth, hence the name."); +INSERT INTO plants (name, common_name, family, description) VALUES ("Fritillaria meleagris", "Snake's-head fritillary", "Liliaceae", "It takes its name from the mottled pattern on its flower that resembles snakeskin."); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Cyclamen coum", "Cyclamen", "Primulaceae", "A winter-flowering corm that provides excellent winter colour.", "cyc_cou.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Tulipa turkestanica", "Tulip", "Liliaceae", "A delicate little tulip that appears in mid spring.", "tul_tur.jpg"); +INSERT INTO plants (name, common_name, family, description, image) VALUES ("Ranunculus ficaria", "Lesser celandine", "Ranunculacae", "A dazzlingly yellow flower that is often considered a weed.", "ran_fic.jpg"); \ No newline at end of file diff --git a/ch12/src/shared/conf/database.properties b/ch12/src/shared/conf/database.properties new file mode 100644 index 0000000..ab7b216 --- /dev/null +++ b/ch12/src/shared/conf/database.properties @@ -0,0 +1,7 @@ +database.root=jdbc:mysql://localhost:3306/ +database.name=plants +driver.name=com.mysql.jdbc.Driver + +create.sql=SQL/plants.sql +insert.sql=SQL/plantsInsert.sql +drop.sql=SQL/plantsDrop.sql \ No newline at end of file diff --git a/ch12/src/shared/docs/LICENSE b/ch12/src/shared/docs/LICENSE new file mode 100644 index 0000000..f820d4b --- /dev/null +++ b/ch12/src/shared/docs/LICENSE @@ -0,0 +1,203 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ch12/src/shared/docs/README b/ch12/src/shared/docs/README new file mode 100644 index 0000000..f629ac0 --- /dev/null +++ b/ch12/src/shared/docs/README @@ -0,0 +1,4 @@ +Each chapter's build file adds to the previous chapter's. + +Therefore, by Chapter 12, the build file contains all the targets and tasks +from the whole book. \ No newline at end of file diff --git a/ch12/src/shared/docs/index.html b/ch12/src/shared/docs/index.html new file mode 100644 index 0000000..dd2219c --- /dev/null +++ b/ch12/src/shared/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the Plant Application.

+ + + diff --git a/ch12/src/shared/java/org/mwrm/plants/Constants.java b/ch12/src/shared/java/org/mwrm/plants/Constants.java new file mode 100644 index 0000000..74b6e93 --- /dev/null +++ b/ch12/src/shared/java/org/mwrm/plants/Constants.java @@ -0,0 +1,47 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +/** + * The Constants class contains four constants + * that represent sort options. + * + */ + +public class Constants { + + /** Use to sort the plants by their botanical name. */ + public static final int SORT_BY_NAME = 1; + + /** Use to sort the plants by their common name. */ + public static final int SORT_BY_COMMON_NAME = 2; + + /** Use to sort the plants by their family name. */ + public static final int SORT_BY_FAMILY = 3; + + /** + * Use to sort the plants by their botanical name + * and exclude those plants that do not begin with the chosen letter. + */ + public static final int SORT_BY_CHOSEN_LETTER = 4; + + /** + * A simple constructor. + */ + public Constants() { } + +} diff --git a/ch12/src/shared/java/org/mwrm/plants/PropertiesLoader.java b/ch12/src/shared/java/org/mwrm/plants/PropertiesLoader.java new file mode 100644 index 0000000..10d07f3 --- /dev/null +++ b/ch12/src/shared/java/org/mwrm/plants/PropertiesLoader.java @@ -0,0 +1,53 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.util.Properties; + +import java.io.IOException; + +/** + * The PropertiesLoader class loads properties + * from the database.properties file and passes them + * to whichever class wants to use them. This centralises the name + * of the properties file so the entire application can use it. + */ + +public class PropertiesLoader { + + /** A simple constructor. */ + public PropertiesLoader() { } + + /** + * Loads the properties for whichever class needs them. + * + * @return A Java properties file + */ + public final Properties loadProperties() { + + // Read properties file. + Properties properties = new Properties(); + try { + properties.load(this.getClass().getClassLoader() + .getResourceAsStream("database.properties")); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + + return properties; + } +} diff --git a/ch12/src/shared/java/org/mwrm/plants/SelectData.java b/ch12/src/shared/java/org/mwrm/plants/SelectData.java new file mode 100644 index 0000000..5a0ad89 --- /dev/null +++ b/ch12/src/shared/java/org/mwrm/plants/SelectData.java @@ -0,0 +1,156 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Properties; + +/** + *

The SelectData class establishes a connection + * with a database and executes a query, as selected by the client.

+ * + *

It gets the database driver name and the URL + * from the database.properties file. + * When the results come back from the database, + * this class places them as HashMap records + * in a Vector. It then passes this Vector + * back to the calling client.

+ * + *

The SQL strings are:

+ * + *
    + *
  • Constants.SORT_BY_NAME (the default): + * SELECT * FROM plants ORDER BY name
  • + *
  • Constants.SORT_BY_COMMON_NAME: + * SELECT * FROM plants ORDER BY common_name
  • + *
  • Constants.SORT_BY_FAMILY: + * SELECT * FROM plants ORDER BY family, name
  • + *
  • Constants.SORT_BY_CHOSEN_LETTER: + * SELECT * FROM plants WHERE name REGEXP '^X' + * where X is a letter supplied by the client
  • + *
+ */ + +public final class SelectData { + + /** + *

The default constructor.

+ */ + private SelectData() { } + + /** + * Get the data from the database. + * @param choice The criteria for sorting the results. + * This choice is held in the Constants class. + * @param letter The letter to use when limiting the search, + * should that option be chosen. + * @return Vector + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there's a problem with database operations + */ + public static Vector getData(final int choice, final String letter) + throws ClassNotFoundException, SQLException { + + // Read properties file + PropertiesLoader loader = new PropertiesLoader(); + Properties properties = loader.loadProperties(); + + // First load the MySQL JDBC driver + Class.forName(properties.getProperty("driver.name")); + + // The datasource + String url = properties.getProperty("database.root") + + properties.getProperty("database.name"); + + // Open the connection + Connection con = DriverManager.getConnection(url, "antBook", "antB00k"); + + Statement stmt = con.createStatement(); + + String select = getSelectString(choice, letter); + + // Now we get the data + ResultSet rs = stmt.executeQuery(select); + + // We'll need the metadata when we come to populate the session object + ResultSetMetaData rsmd = rs.getMetaData(); + int numberOfColumns = rsmd.getColumnCount(); + + Vector results = new Vector(); + + while (rs.next()) { + // We need a fresh entry every time + HashMap record = new HashMap(numberOfColumns); + + String columnName = ""; + + // For each column in the table, + // we want to add an entry to the HashMap + // with the same key as the column name + for (int i = 1; i <= numberOfColumns; i++) { + columnName = rsmd.getColumnName(i); + record.put(columnName, rs.getString(columnName)); + } + results.add(record); + } + + // Close the Statement and the Connection + stmt.close(); + con.close(); + + return results; + } + + /** + *

Returns the appropriate SQL string for the choice.

+ * @param choice The user's choice of search criteria. + * @param letter The letter to use when modifying the search. + * @return String + */ + private static String + getSelectString(final int choice, final String letter) { + + // This is the default SELECT statement if no arguments are specified + String selectString = "SELECT * FROM plants ORDER BY name"; + + // Check the type of argument + if (choice == Constants.SORT_BY_COMMON_NAME) { + // Order the results by common name + selectString = "SELECT * FROM plants ORDER BY common_name"; + + } else if (choice == Constants.SORT_BY_FAMILY) { + // Order the results by family, then botanical name + selectString = "SELECT * FROM plants ORDER BY family, name"; + + } else if (choice == Constants.SORT_BY_CHOSEN_LETTER) { + // The search will only return those plants whose botanical name + // begins with the specifed letter. + selectString = "SELECT * FROM plants WHERE name REGEXP '^" + + letter + "'"; + } + + return selectString; + } +} diff --git a/ch12/src/shared/java/org/mwrm/plants/package.html b/ch12/src/shared/java/org/mwrm/plants/package.html new file mode 100644 index 0000000..b64e7c3 --- /dev/null +++ b/ch12/src/shared/java/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants Package + + +

Utility classes for the plant application.

+ + \ No newline at end of file diff --git a/ch12/src/stand-alone/docs/index.html b/ch12/src/stand-alone/docs/index.html new file mode 100644 index 0000000..f615152 --- /dev/null +++ b/ch12/src/stand-alone/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the stand-alone application.

+ + + \ No newline at end of file diff --git a/ch12/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java b/ch12/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java new file mode 100644 index 0000000..bca1073 --- /dev/null +++ b/ch12/src/stand-alone/java/org/mwrm/plants/client/PlantClient.java @@ -0,0 +1,154 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.client; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + +/** + *

The PlantClient class is a command-line client + * for the plant application.

+ *

Usage:

+ *
    + *
  • -c Order by common name
  • + *
  • -f Order by family
  • + *
  • -n Order by botanical name (default)
  • + *
  • -n [letter] Order by botanical name + * and limit the search to plants beginning with the specified letter
  • + *
+ */ + +public final class PlantClient { + + /** + *

A simple constructor.

+ */ + private PlantClient() { } + + /** + * Checks the arguments, + * then uses the org.mwrm.plants.SelectData + * class to get results from the database. + * Once it has the results, it displays them to standard out. + * @param args The command-line arguments. + * @throws ClassNotFoundException If the database driver is not found + * @throws SQLException If there is a problem with the database + */ + public static void main(final String[] args) + throws ClassNotFoundException, SQLException { + + // The default choice + int choice = Constants.SORT_BY_NAME; + + // The user may want to select by a certain letter + String letter = ""; + + // Check that an argument has been provided + if (args.length > 0) { + // Check the type of argument + if (args[0].equals("-c") || args[0].equals("")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by common name + choice = Constants.SORT_BY_COMMON_NAME; + + } else if (args[0].equals("-f")) { + // Cannot be used with an argument just now, + // though Ant may pass an empty string + if (args.length > 1 && !args[1].equals("")) { + usage(); + } + // Order the results by family, then botanical name + choice = Constants.SORT_BY_FAMILY; + + } else if (args[0].equals("-n")) { + // Order the results by botanical name + // This is the default if no arguments are specified + + // The user can provide another argument. + // The search will only return those plants whose botanical name + // begins with the specifed letter. + if (args.length > 1 && !args[1].equals("")) { + choice = Constants.SORT_BY_CHOSEN_LETTER; + letter = args[1]; + } + } else { + // Usage information + usage(); + } + } + + // Obtain the results. This is a Vector of HashMaps + Vector results = SelectData.getData(choice, letter); + + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + System.out.println("Name: " + record.get("name") + cultivar); + System.out.println("Common name: " + record.get("common_name")); + System.out.println("Family: " + record.get("family")); + System.out.println("Description: " + record.get("description")); + System.out.println("-----------------------------"); + } + } + } + + /** + *

Print the usage information.

+ */ + private static void usage() { + System.out.println("\nUsage: \n"); + System.out.println("-c \t\t Order by common name"); + System.out.println("-f \t\t Order by family"); + System.out.println("-n \t\t Order by botanical name (default)"); + System.out.println("-n [letter] \t Order by botanical name" + + " and limit the search to plants "); + System.out.println("\t\t beginning with the specified letter"); + System.exit(0); + } +} diff --git a/ch12/src/stand-alone/java/org/mwrm/plants/client/package.html b/ch12/src/stand-alone/java/org/mwrm/plants/client/package.html new file mode 100644 index 0000000..9148475 --- /dev/null +++ b/ch12/src/stand-alone/java/org/mwrm/plants/client/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.client Package + + +

Contains the command-line client for the plant application.

+ + \ No newline at end of file diff --git a/ch12/src/web/conf/antBook.xml b/ch12/src/web/conf/antBook.xml new file mode 100644 index 0000000..9746019 --- /dev/null +++ b/ch12/src/web/conf/antBook.xml @@ -0,0 +1,3 @@ + + diff --git a/ch12/src/web/conf/plant.tld b/ch12/src/web/conf/plant.tld new file mode 100644 index 0000000..0066066 --- /dev/null +++ b/ch12/src/web/conf/plant.tld @@ -0,0 +1,32 @@ + + + + + 1.0 + 2.0 + plant + /plant-taglib + + A simple tab library for the plant application + + + + + Displays a character when given an integer + + letters + org.mwrm.plants.tags.LettersTag + false + none + + letter + true + true + char + + + + \ No newline at end of file diff --git a/ch12/src/web/conf/web.xml b/ch12/src/web/conf/web.xml new file mode 100644 index 0000000..c60d60e --- /dev/null +++ b/ch12/src/web/conf/web.xml @@ -0,0 +1,66 @@ + + + + + A plant application. + + + + resultsPerPage + 5 + + This value sets how many results will be shown per results page. + + + + + + + Resource reference to a factory for java.sql.Connection + instances that may be used for talking to a particular + database that is configured in the plantBook.xml file. + + + jdbc/PlantDB + + + javax.sql.DataSource + + + SERVLET + + + + + /plant-taglib + /WEB-INF/plant.tld + + + + plantServlet + + org.mwrm.plants.servlets.PlantServlet + + + + + Print debug information to the console + + + debug + + + true + + + + + + plantServlet + /plants/listPlants.jsp + + + \ No newline at end of file diff --git a/ch12/src/web/docs/index.html b/ch12/src/web/docs/index.html new file mode 100644 index 0000000..606c2b3 --- /dev/null +++ b/ch12/src/web/docs/index.html @@ -0,0 +1,19 @@ + + + + Welcome to the Plant Application + + + + + + + + +
+ The stand-alone application
+ The web application
+

This is the web application.

+ + + \ No newline at end of file diff --git a/ch12/src/web/images/banner_under.jpg b/ch12/src/web/images/banner_under.jpg new file mode 100644 index 0000000..131c6a1 Binary files /dev/null and b/ch12/src/web/images/banner_under.jpg differ diff --git a/ch12/src/web/images/cyc_cou.jpg b/ch12/src/web/images/cyc_cou.jpg new file mode 100644 index 0000000..ed0556c Binary files /dev/null and b/ch12/src/web/images/cyc_cou.jpg differ diff --git a/ch12/src/web/images/gal_niv.jpg b/ch12/src/web/images/gal_niv.jpg new file mode 100644 index 0000000..efd2492 Binary files /dev/null and b/ch12/src/web/images/gal_niv.jpg differ diff --git a/ch12/src/web/images/no_image.jpg b/ch12/src/web/images/no_image.jpg new file mode 100644 index 0000000..e86c254 Binary files /dev/null and b/ch12/src/web/images/no_image.jpg differ diff --git a/ch12/src/web/images/ran_fic.jpg b/ch12/src/web/images/ran_fic.jpg new file mode 100644 index 0000000..1772557 Binary files /dev/null and b/ch12/src/web/images/ran_fic.jpg differ diff --git a/ch12/src/web/images/tul_tur.jpg b/ch12/src/web/images/tul_tur.jpg new file mode 100644 index 0000000..fcc77b2 Binary files /dev/null and b/ch12/src/web/images/tul_tur.jpg differ diff --git a/ch12/src/web/java/org/mwrm/plants/servlets/PlantServlet.java b/ch12/src/web/java/org/mwrm/plants/servlets/PlantServlet.java new file mode 100644 index 0000000..bc9b9a8 --- /dev/null +++ b/ch12/src/web/java/org/mwrm/plants/servlets/PlantServlet.java @@ -0,0 +1,209 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.servlets; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Vector; +import java.util.Enumeration; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import javax.servlet.ServletException; + +import org.mwrm.plants.SelectData; +import org.mwrm.plants.Constants; + + /** + *

The servlet client for the plant application.

+ * + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ */ +public class PlantServlet extends HttpServlet { + + /** + *

Extracts the show parameter from the request + * to determine what the user wants to see. + * Valid values for show are:

+ * + *
    + *
  • common: Order by common name
  • + *
  • family: Order by family
  • + *
  • name: Order by botanical name (default)
  • + *
+ * + *

If the client sends a letter parameter, + * then the search is limited to records that begin with that letter.

+ * + *

Once the choice has been extracted, + * this servlet uses the org.mwrm.plants.SelectData class + * to get results from the database. Once it has the results, + * it places them in the session under the name "results" + * and forwards the request to /plants/displayPage.jsp, + * which displays the first page of the results.

+ * + *

If the debug servlet initialization parameter + * is set to true the results + * will also be sent to standard out.

+ * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // The default choice + int choice = Constants.SORT_BY_NAME; + + // Check the show parameter + // to see if the user wants to select anything else + if (request.getParameter("show").equals("common")) { + choice = Constants.SORT_BY_COMMON_NAME; + } else if (request.getParameter("show").equals("family")) { + choice = Constants.SORT_BY_FAMILY; + } + + // The results from the database + Vector results = null; + // The letter that the user wants to sort by + String letter = null; + + // If the letter parameter is set, then make the appropriate choice + if (request.getParameter("letter") != null) { + letter = request.getParameter("letter").toLowerCase(); + choice = Constants.SORT_BY_CHOSEN_LETTER; + } + + // Obtain the results. This is a Vector of HashMaps + try { + results = SelectData.getData(choice, letter); + } catch (ClassNotFoundException cnfe) { + throw new ServletException(cnfe.getMessage()); + } catch (SQLException sqle) { + throw new ServletException(sqle.getMessage()); + } + + // The debug servlet initialization parameter sets output options + if (getServletConfig().getInitParameter("debug").equals("true")) { + // The top of the results display + System.out.println("\n-----------------------------"); + + // If there is no data in the results, tell the user + if (results.isEmpty()) { + System.out.println("No results found."); + System.out.println("-----------------------------"); + } else { + + // Each record in the database is a HashMap + HashMap record = new HashMap(); + + // Iterate over the results + for (Enumeration enum = results.elements(); + enum.hasMoreElements();) { + + // The current record + record = (HashMap) enum.nextElement(); + + // The cultivar name is optional + String cultivar = ""; + + // If the cultivar_name column is present, + // add it to the output string + if (!(record.get("cultivar_name") == null)) { + cultivar = " '" + record.get("cultivar_name") + "'"; + } + + // Print them to standard out + System.out.println("Name: " + + record.get("name") + cultivar); + System.out.println("Common name: " + + record.get("common_name")); + System.out.println("Family: " + + record.get("family")); + System.out.println("Description: " + + record.get("description")); + System.out.println("Image: " + + record.get("image")); + System.out.println("-----------------------------"); + } + } + } + + // Now store the results in the session + HttpSession session = request.getSession(true); + + // If the results are empty, + // then store a flag to tell the application as such, + // then remove the results object so the pages don't try to work with it + if (results.isEmpty()) { + session.setAttribute("noResults", "true"); + session.removeAttribute("results"); + + // There are some results, + // so place them in the session and remove the noResults flag, + // so that the application knows there are results + } else { + session.setAttribute("results", results); + session.removeAttribute("noResults"); + + // The pages will need to know the number of results + session.setAttribute("resultsSize", "" + results.size() + ""); + } + + // Forward the request to the display page + getServletContext() + .getRequestDispatcher("/plants/displayResults.jsp?start=0") + .forward(request, response); + } + + /** + * Sends any POST requests to the doGet method. + * + * @param request The request object. + * @param response The response object. + * + * @throws ServletException + * If there is a problem when processing the request + * @throws IOException If there is a problem writing the response + */ + public final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + doGet(request, response); + } +} diff --git a/ch12/src/web/java/org/mwrm/plants/servlets/package.html b/ch12/src/web/java/org/mwrm/plants/servlets/package.html new file mode 100644 index 0000000..d9ac8dd --- /dev/null +++ b/ch12/src/web/java/org/mwrm/plants/servlets/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.servlets Package + + +

Contains the servlet for the plant application.

+ + \ No newline at end of file diff --git a/ch12/src/web/java/org/mwrm/plants/tags/LettersTag.java b/ch12/src/web/java/org/mwrm/plants/tags/LettersTag.java new file mode 100644 index 0000000..6911f3f --- /dev/null +++ b/ch12/src/web/java/org/mwrm/plants/tags/LettersTag.java @@ -0,0 +1,63 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants.tags; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import javax.servlet.jsp.tagext.SimpleTagSupport; + +import java.io.IOException; + + /** + *

Converts an integer into a character. + * The letter attribute takes the integer, + * which is converted to a char by the time the tag gets it. + * The tag then writes the char to the client.

+ */ +public class LettersTag extends SimpleTagSupport { + + /** + * The character to display. + */ + private char letter; + + /** + *

Processes the tag when it is encountered on the page.

+ * @throws JspException + * If there is a problem processing the tag + * @throws IOException + * If there is a problem writing to the client + */ + public final void doTag() throws JspException, IOException { + + // The page that the client will receive + JspWriter out = getJspContext().getOut(); + + // Write the letter to the client + out.print(letter); + } + + /** + * + *

The setter method for the letter attribute.

+ * + * @param aLetter The letter to display. + */ + public final void setLetter(final char aLetter) { + this.letter = aLetter; + } +} diff --git a/ch12/src/web/java/org/mwrm/plants/tags/package.html b/ch12/src/web/java/org/mwrm/plants/tags/package.html new file mode 100644 index 0000000..fcb8df4 --- /dev/null +++ b/ch12/src/web/java/org/mwrm/plants/tags/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants.tags Package + + +

Contains the custom tags for the plant application.

+ + \ No newline at end of file diff --git a/ch12/src/web/pages/footer.html b/ch12/src/web/pages/footer.html new file mode 100644 index 0000000..8511c9c --- /dev/null +++ b/ch12/src/web/pages/footer.html @@ -0,0 +1,3 @@ +
+ Bottom banner +
\ No newline at end of file diff --git a/ch12/src/web/pages/header.jsp b/ch12/src/web/pages/header.jsp new file mode 100644 index 0000000..135f4a5 --- /dev/null +++ b/ch12/src/web/pages/header.jsp @@ -0,0 +1,16 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib uri="/plant-taglib" prefix="tags" %> +
\ No newline at end of file diff --git a/ch12/src/web/pages/index.jsp b/ch12/src/web/pages/index.jsp new file mode 100644 index 0000000..2b9ae28 --- /dev/null +++ b/ch12/src/web/pages/index.jsp @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ch12/src/web/pages/indexBody.jsp b/ch12/src/web/pages/indexBody.jsp new file mode 100644 index 0000000..6410604 --- /dev/null +++ b/ch12/src/web/pages/indexBody.jsp @@ -0,0 +1 @@ +

Welcome to the plant information application.

\ No newline at end of file diff --git a/ch12/src/web/pages/menu.jsp b/ch12/src/web/pages/menu.jsp new file mode 100644 index 0000000..135b8cc --- /dev/null +++ b/ch12/src/web/pages/menu.jsp @@ -0,0 +1,5 @@ +<%-- This page is common to the whole application --%> +<%@ taglib tagdir="/WEB-INF/tags" prefix="tags" %> +
+ +
\ No newline at end of file diff --git a/ch12/src/web/pages/plantStyle.css b/ch12/src/web/pages/plantStyle.css new file mode 100644 index 0000000..48b3129 --- /dev/null +++ b/ch12/src/web/pages/plantStyle.css @@ -0,0 +1,75 @@ +.menu-main { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.menu-sub { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.footer { + color: #000000; + background-color: #FFFFFF; +} + +h1 { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: large; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.letters { + color: #000000; + background-color: #FFFFFF; + margin: 3px 5px 5px 3px; + font-size: medium; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} + +.center { + text-align: center +} + +tr.template { + height: 500px +} + +td.template { + width: 200px +} + +tr.resultRow { + height: 250px +} + +p.left { + text-align: left +} + +p { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif +} + +.italic { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; + font-style: italic +} + +.results { + color: #000000; + background-color: #FFFFFF; + font-family: Arial, Verdana,Helvetica, Sans-Serif; +} diff --git a/ch12/src/web/pages/plants/displayPlant.jsp b/ch12/src/web/pages/plants/displayPlant.jsp new file mode 100644 index 0000000..6e30cd6 --- /dev/null +++ b/ch12/src/web/pages/plants/displayPlant.jsp @@ -0,0 +1,15 @@ +<%-- JSP Directives --%> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + diff --git a/ch12/src/web/pages/plants/displayPlantBody.jsp b/ch12/src/web/pages/plants/displayPlantBody.jsp new file mode 100644 index 0000000..9c37af4 --- /dev/null +++ b/ch12/src/web/pages/plants/displayPlantBody.jsp @@ -0,0 +1,31 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + + + + + + + + + + + +
+

+ Name:
+ Common name:
+ Family:
+ Description:
+

+
+
+ +<%-- We need to go back to where we came from --%> +Back \ No newline at end of file diff --git a/ch12/src/web/pages/plants/displayResults.jsp b/ch12/src/web/pages/plants/displayResults.jsp new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/ch12/src/web/pages/plants/displayResults.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch12/src/web/pages/plants/displayResultsBody.jsp b/ch12/src/web/pages/plants/displayResultsBody.jsp new file mode 100644 index 0000000..0c09a66 --- /dev/null +++ b/ch12/src/web/pages/plants/displayResultsBody.jsp @@ -0,0 +1,63 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + +

Search Results

+ + + +

Sorry, there were no results for the search. Please try again.

+
+ + +
+

Number of results:

+ + + + + + + +
+ + "> +
+ +
+
+
+ + + + + + + ">First + + + + + + + ">Back + + + + + + + ">Next + + + + + + + ">Last + +
+
+ + \ No newline at end of file diff --git a/ch12/src/web/pages/plants/index.jsp b/ch12/src/web/pages/plants/index.jsp new file mode 100644 index 0000000..3c51457 --- /dev/null +++ b/ch12/src/web/pages/plants/index.jsp @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ch12/src/web/pages/plants/searchForm.jsp b/ch12/src/web/pages/plants/searchForm.jsp new file mode 100644 index 0000000..0043e00 --- /dev/null +++ b/ch12/src/web/pages/plants/searchForm.jsp @@ -0,0 +1 @@ +

Click on a letter above or a link on the left.

diff --git a/ch12/src/web/pages/template.jsp b/ch12/src/web/pages/template.jsp new file mode 100644 index 0000000..a1fff41 --- /dev/null +++ b/ch12/src/web/pages/template.jsp @@ -0,0 +1,47 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + + + + + + + <c:out value="${param.title}" default="Plants"/> + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + +
+
+ + + \ No newline at end of file diff --git a/ch12/src/web/tags/simple.tag b/ch12/src/web/tags/simple.tag new file mode 100644 index 0000000..8683c7f --- /dev/null +++ b/ch12/src/web/tags/simple.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +

+ Home
+ Plants
+ + +  By common name
+  By botanical name
+  By family
+
+

\ No newline at end of file diff --git a/ch12/test/org/mwrm/plants/PlantWebTest.java b/ch12/test/org/mwrm/plants/PlantWebTest.java new file mode 100644 index 0000000..f1c5093 --- /dev/null +++ b/ch12/test/org/mwrm/plants/PlantWebTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.mwrm.plants; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.xml.sax.SAXException; + +import junit.framework.TestCase; + +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebResponse; + +/** + *

Tests the plant servlet. + * It checks that the web application is running + * and then checks the session is emptied + * if the there are no results in the query.

+ */ +public class PlantWebTest extends TestCase { + + /** The URL of the server. */ + private static final String SERVER_URL = "http://localhost:8080"; + + /** The web application's name. */ + private static final String WEB_APP = "/antBook"; + + /** The response code we're looking for. */ + private static final int RESPONSE_CODE = 200; + + /** + *

The constructor, + * which simply calls super(name).

+ * @param name The name of the test + */ + public PlantWebTest(final String name) { + super(name); + } + + /** + *

We want to make sure that the web application is running.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testIsRunning() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + try { + // Send a request to the web application's root + WebResponse resp = wc.getResponse(SERVER_URL + WEB_APP); + + // If there is a 200 return code, then it is available + assertEquals("Web application not available at " + + SERVER_URL + WEB_APP, + RESPONSE_CODE, resp.getResponseCode()); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } + + /** + *

The application should detect that no results have been obtained.

+ * @throws MalformedURLException + * If the URL of the web server is not correct + * @throws SAXException + * If the response can't be processed properly + */ + public final void testSession() + throws MalformedURLException, SAXException { + // Create a WebConversation object + WebConversation wc = new WebConversation(); + + try { + // First send a request that will not produce any results + WebResponse resp = + wc.getResponse(SERVER_URL + WEB_APP + + "/plants/listPlants.jsp?show=name&letter=X"); + // Check that this is the case + assertTrue("Session not cancelled after empty results", + (resp.getText().indexOf("Sorry") > -1)); + + // Now go to the index page, + // where there should not be an error message + resp = wc.getResponse(SERVER_URL + WEB_APP + "/plants/"); + assertTrue("Session not cancelled after empty results", + !(resp.getText().indexOf("Sorry") > -1)); + } catch (IOException ioe) { + // We can't find the server, so we fail the test + fail("Server not available"); + } + } +} diff --git a/ch12/test/org/mwrm/plants/package.html b/ch12/test/org/mwrm/plants/package.html new file mode 100644 index 0000000..a9c3f06 --- /dev/null +++ b/ch12/test/org/mwrm/plants/package.html @@ -0,0 +1,8 @@ + + + org.mwrm.plants + + +

Contains the test classes for the plant application.

+ + diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file