ebook img

Pro Apache Ant PDF

344 Pages·2006·2.22 MB·English
Save to my drive
Quick download
Download
Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.

Preview Pro Apache Ant

Pro Apache Ant ■ ■ ■ Matthew Moodie Pro Apache Ant Copyright © 2006 by Matthew Moodie All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. ISBN (pbk): 1-59059-559-9 Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1 Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Lead Editor: Steve Anglin Technical Reviewer: Carsten Ziegeler Editorial Board: Steve Anglin, Dan Appleman, Ewan Buckingham, Gary Cornell, Tony Davis, Jason Gilmore, Jonathan Hassell, Chris Mills, Dominic Shakeshaft, Jim Sumser Project Manager: Beth Christmas Copy Edit Manager: Nicole LeClerc Copy Editor: Kim Wimpsett Assistant Production Director: Kari Brooks-Copony Production Editor: Laura Cheu Compositor: Susan Glinert Proofreader: Kim Burton Indexer: Carol Burbo Artist: Kinetic Publishing Services, LLC Cover Designer: Kurt Krames Manufacturing Director: Tom Debolski Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail [email protected], or visit http://www.springeronline.com. For information on translations, please contact Apress directly at 2560 Ninth Street, Suite 219, Berkeley, CA 94710. Phone 510-549-5930, fax 510-549-5939, e-mail [email protected], or visit http://www.apress.com. The information in this book is distributed on an “as is” basis, without warranty. Although every precaution has been taken in the preparation of this work, neither the author(s) nor Apress shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in this work. The source code for this book is available to readers at http://www.apress.com in the Source Code section. To Laura Contents at a Glance About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xiii About the Technical Reviewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii ■CHAPTER 1 Introducing Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 ■CHAPTER 2 Installing Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 ■CHAPTER 3 Using Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 ■CHAPTER 4 Examining Ant’s Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 ■CHAPTER 5 Building a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 ■CHAPTER 6 Deploying an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 ■CHAPTER 7 Running an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 ■CHAPTER 8 Testing an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 ■CHAPTER 9 Using Ant in Large Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 ■CHAPTER 10 Writing Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 ■CHAPTER 11 Extending Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 ■CHAPTER 12 Using the Ant API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 ■INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 v CCoonntteennttss About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xiii About the Technical Reviewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii ■CHAPTER 1 Introducing Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Organizing Complex Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Compiling Simple Projects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Compiling Larger Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Introducing the Build Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Introducing make . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Introducing Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Introducing Ant Targets and Tasks. . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 ■CHAPTER 2 Installing Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Installing a Binary Ant Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Downloading a Binary Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Unpacking the Binary Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Verifying the Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Using PGP to Verify the Binary Distribution . . . . . . . . . . . . . . . . . . . . 14 Using MD5 and SHA1 to Verify the Download. . . . . . . . . . . . . . . . . . 19 Installing a Source Ant Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Downloading a Source Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Using CVS to Obtain a Source Distribution. . . . . . . . . . . . . . . . . . . . . 26 Building the Ant Source Distribution with the Build Script. . . . . . . . 26 Taking Final Steps After Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Setting %ANT_HOME% on Windows . . . . . . . . . . . . . . . . . . . . . . . . . 28 Setting $ANT_HOME on Unix. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 vii viii ■CONTENTS Examining the Ant Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Looking at the bin Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Looking at the docs Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Looking at the etc Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Looking at the lib Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Upgrading Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 ■CHAPTER 3 Using Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Running Ant from the Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Introducing Ant’s Build File Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Examining the Project Element. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Examining the Target Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Working with Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Using Built-in Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Setting Properties in the Build File . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Setting Properties in Property Files . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Summarizing the Property Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Setting Properties at the Command Line. . . . . . . . . . . . . . . . . . . . . . 56 Examining Property Precedence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Using Properties to Control a Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Using the Available Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Using the Uptodate Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Using the Condition Task. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Working with Property Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Using Pathlike Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Setting a Pathlike Structure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Setting a Classpath Pathlike Structure. . . . . . . . . . . . . . . . . . . . . . . . 74 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 ■CHAPTER 4 Examining Ant’s Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Using Directory-Based Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Using Pattern Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Working with Directory Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Working with File Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Working with Class File Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Working with File Lists. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Working with Zip File Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 ■CONTENTS ix ■CHAPTER 5 Building a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 Introducing the Example Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 Introducing the Shared Code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Introducing the Third-Party Libraries . . . . . . . . . . . . . . . . . . . . . . . . 101 Introducing the Stand-Alone Application . . . . . . . . . . . . . . . . . . . . . 102 Introducing the Web Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Introducing the Final Directory Structure. . . . . . . . . . . . . . . . . . . . . 103 Compiling Java Applications with Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Setting Up a Working Environment. . . . . . . . . . . . . . . . . . . . . . . . . . 104 Adding Third-Party Libraries to the Build. . . . . . . . . . . . . . . . . . . . . 111 Assembling the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Manipulating File Location. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Creating the JAR Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Creating WAR Files. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 Building the Example Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 ■CHAPTER 6 Deploying an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 Building Documentation Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 Creating Javadocs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Finishing the Bundle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Writing Ant Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 Creating Zip and Tar Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Zipping the Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Tarring the Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Using the Zip and Tar Build Paths. . . . . . . . . . . . . . . . . . . . . . . . . . . 156 Distributing the Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 Placing the Application on an FTP Server . . . . . . . . . . . . . . . . . . . . 156 Distributing the Application via E-mail. . . . . . . . . . . . . . . . . . . . . . . 161 Deploying a Web Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 ■CHAPTER 7 Running an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Using SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Running Java Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Running the Stand-Alone Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 Redirecting Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 x ■CONTENTS Running Native Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 Starting Tomcat with Ant. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Creating PGP Hashes with Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 ■CHAPTER 8 Testing an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Testing by Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Testing with JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Installing the Testing Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Organizing the Test File Structure . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Initializing the Testing Environment . . . . . . . . . . . . . . . . . . . . . . . . . 190 Compiling the Test Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Testing the Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Testing Code Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 Using the <checkstyle> Task. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Transforming XML to HTML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 ■CHAPTER 9 Using Ant in Large Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Using Master Build Files and Ant Delegation . . . . . . . . . . . . . . . . . . . . . . 210 Moving Ant Tasks to Subordinate Build Files . . . . . . . . . . . . . . . . . . . . . 211 Preparing for the Move. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Moving the Third-Party Build Targets. . . . . . . . . . . . . . . . . . . . . . . . 212 Moving the Shared Build Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Moving the Application-Specific Build Targets . . . . . . . . . . . . . . . . 215 Moving the Packaging Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 Moving the Test Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Changing the Master Build File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Running Individual Subordinate Targets . . . . . . . . . . . . . . . . . . . . . 222 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 ■CHAPTER 10 Writing Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Examining Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Introducing the Custom Task Life Cycle. . . . . . . . . . . . . . . . . . . . . . 226 Introducing the Custom Task API . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 Working with Nested Elements in Tasks . . . . . . . . . . . . . . . . . . . . . . . . . 243 Writing an addXXX() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 Writing an addConfiguredXXX() Method. . . . . . . . . . . . . . . . . . . . . . 250 ■CONTENTS xi Writing a createXXX() Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Choosing Which Method to Use . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Writing Example Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Providing Usage Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Extending the <javadoc> Task. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Using an antlib File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 Using Third-Party Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 ■CHAPTER 11 Extending Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Logging Ant Builds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Sending E-mail Confirmations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Using XML Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 Using a Log4j Logger. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 Writing Your Own Listener. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 Writing Your Own Logger. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 Using the Ant-Contrib Performance Listener. . . . . . . . . . . . . . . . . . 286 Using Mappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Using Identity Mappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Using Flatten Mappers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 Using Merge Mappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 Using Glob Mappers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 Using Regexp Mappers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 Using Chained Mappers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 ■CHAPTER 12 Using the Ant API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 Designing a Class to Use the Ant API . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 Working with the Task Life Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 Choosing a Task. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 Writing a Usage Check. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 Using a Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Adding Loggers and Listeners. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Writing a Batch Copy Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 ■INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 About the Author ■MATTHEW MOODIE is a native of southwest Scotland and is a graduate of the University of Edin- burgh, where he obtained a master’s degree in linguistics and artificial intelligence. Matthew enjoys a life of fun in Glasgow, Scotland. He is a keen novice gardener with a house full of plants. xiii

Description:
Pro Apache Ant is ideal for Java developers who need to use the leading open source build tool out there for development and project management. It is organized around chronological tasks instead of alphabetical functions, using a sample application throughout the set-up, from calling database scrip
See more

The list of books you might like

Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.