Essential GWT Building for the Web with Google Web


ptg Download from www.wowebook.com ptg Essential GWT Download from www.wowebook.com ptg Essential GWT Building for the Web with Google Web Toolkit 2 Federico Kereki Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris • Madrid Capetown • Sydney • Tokyo • Singapore • Mexico City Download from www.wowebook.com ptg Editor-in-Chief Mark Taub Acquisitions Editor Trina MacDonald Development Editor Songlin Qiu Managing Editor John Fuller Project Editor Anna Popick Copy Editor Apostrophe Editing Services Indexer Jack Lewis Proofreader Linda Begley Editorial Assistant Olivia Basegio Technical Reviewers Jason Essington Jim Hathaway Daniel Wellman Cover Designer Gary Adair Compositor Rob Mauhar Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital let- ters or in all capitals. The author and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omis- sions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. The publisher offers excellent discounts on this book when ordered in quantity for bulk pur- chases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U.S. Corporate and Government Sales (800) 382-3419 corpsales@pearsontechgroup.com For sales outside the United States please contact: International Sales international@pearson.com Visit us on the Web: informit.com/aw Library of Congress Cataloging-in-Publication Data Kereki, Federico, 1960- Essential GWT : building for the web with Google Web toolkit 2 / Federico Kereki. p. cm. Includes index. ISBN-13: 978-0-321-70514-3 (pbk. : alk. paper) ISBN-10: 0-321-70514-9 (pbk. : alk. paper) 1. Ajax (Web site development technology) 2. Java (Computer program language) 3. Google Web toolkit. 4. Application software--Development. I. Title. TK5105.8885.A52K47 2011 006.7'6--dc22 2010018606 Copyright © 2011 Pearson Education, Inc. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding per- missions, write to: Pearson Education, Inc. Rights and Contracts Department 501 Boylston Street, Suite 900 Boston, MA 02116 Fax: (617) 671-3447 ISBN-13: 978-0-321-70514-3 ISBN-10: 0-321-70514-9 Text printed in the United States on recycled paper at RR Donnelley in Crawfordsville, IN. First printing, July 2010 Download from www.wowebook.com ptg ❖ To my parents, Eugenio Kereki and Susana Guerrero, who got me started on my way, and always stood by me, and to my wife, Sylvia Tosar, who had to bear without a husband while I wrote the book, who nevertheless kept the family and home going on, and without whom I wouldn’t want to go anywhere. ❖ Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg Contents at a Glance Preface xv Acknowledgments xix About the Author xxi 1 Developing Your Application 1 2 Getting Started with GWT 2 9 3 Understanding Projects and Development 21 4 Working with Browsers 31 5 Programming the User Interface 55 6 Communicating with Your Server 77 7 Communicating with Other Servers 119 8 Mixing in JavaScript 139 9 Adding APIs 157 10 Working with Servers 177 11 Moving Around Files 195 12 Internationalization and Localization 211 13 Testing Your GWT Application 229 14 Optimizing for Application Speed 259 15 Deploying Your Application 287 Index 301 Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg Contents Preface xv Acknowledgments xix About the Author xxi 1 Developing Your Application 1 Rich Internet Applications 1 Web 2.0 2 Cloud Computing 3 The “Death of the Desktop” 4 Advantages of GWT 4 HTML Ubiquity and Browser Differences 4 JavaScript Deficiencies 5 Software Methodologies to Apply 5 Classic Development Problems 5 Agile Methodologies 7 Forever Beta? 7 Summary 8 2 Getting Started with GWT 2 9 Why Use GWT? 9 Why Java? 10 Some Actual Disadvantages 10 The GWT Components 12 Compiler 12 JRE Emulation Library 14 UI Library 17 Setting Up GWT 17 Writing Code 17 Version Control Management 19 Testing 19 Running and Deploying 19 Summary 20 Download from www.wowebook.com ptg x Contents 3 Understanding Projects and Development 21 Creating a Project 21 Using the Google Plugin for Eclipse 21 Using the GWT Shell Script 22 Project Structure 23 Running Your Application: Development Mode 27 Summary 30 4 Working with Browsers 31 The Back Button Problem 31 Setting Up Your HTML Page 32 The History Class 33 Starting Your Application 34 Showing Forms in Pop-Ups 37 Passing Parameters 38 Creating a Menu 41 Detecting the User’s Browser 43 The Classic Way 43 The Deferred Binding Way 44 Recognizing Older Explorers 52 No JavaScript? 53 Summary 53 5 Programming the User Interface 55 Thinking About UI Patterns 55 MVC: A Classic Pattern 56 MVP: A More Suitable Pattern 57 Implementing MVP 59 Callbacks Galore 59 Implementation Details 60 Some Extensions 67 Declarative UI 69 A Basic UiBinder Example 70 More Complex Examples 73 Summary 76 Download from www.wowebook.com ptg xiContents 6 Communicating with Your Server 77 Introduction to RPC 77 Implementation 78 Serialization 79 Direct Evaluation RPC 83 RPC Patterns of Usage 84 The World Cities Service 84 Code Sharing 86 Coding the Server Side Services 88 Database-Related Widgets and MVP 94 A Look at MVP 100 A Country/State Cities Browser 101 Live Suggestions 108 Data Prevalidation 112 Enterprise Java Beans 116 Summary 118 7 Communicating with Other Servers 119 The Same Origin Policy (SOP) Restriction 119 Our City Update Application 121 Receiving and Processing XML 125 Using Ajax Directly 127 Going Through a Proxy 129 Producing and Sending XML 131 Creating XML with Strings 132 Creating XML Through the DOM 133 Sending the XML Data 135 Sending XML Through Ajax 136 Sending XML Through a Proxy 136 Summary 137 8 Mixing in JavaScript 139 JSNI 139 Basic JSNI Usage 140 Hashing with JavaScript 142 Animations Beyond GWT 143 A Steampunk Display Widget 143 Download from www.wowebook.com ptg xii Contents JSON 146 JSONP 153 Summary 155 9 Adding APIs 157 A Weather Vane 157 Getting Weather Data 157 Getting the Feed 159 Getting Everything Together 160 Getting at the Feed Data with an Overlay 161 Getting the Feed with JSNI 162 Dashboard Visualizations 162 Using the Google Visualization API 164 Handling Events 167 Working with Maps 168 Interactive Maps 168 Fixed Maps 173 Summary 175 10 Working with Servers 177 The Challenges to Meet 177 Before Going Any Further 177 Security 178 Ajax Problems 179 Cryptography 179 Hashing 180 Encrypting 180 Stateless Versus Stateful Servers 183 Common Operations 185 Logging In 185 Changing Your Password 190 Summary 193 11 Moving Around Files 195 Uploading Files 195 An Upload Form 195 A File Processing Servlet 200 Providing Feedback to the User 202 Download from www.wowebook.com ptg xiiiContents Downloading Files 204 A File Download Form 204 A Sample File Producing Servlet 207 Summary 209 12 Internationalization and Localization 211 Internationalization (i18n) 211 Resource Bundles 212 Using Constants 213 Messages 217 UiBinder Internationalization 219 Localization (l10n) 223 Summary 227 13 Testing Your GWT Application 229 Why Testing? 229 Advantages of Automatically Tested Code 230 And if a Bug Appears? 230 Unit Testing with JUnit 231 A Basic JUnit Example 231 Test Coverage with Emma 236 Testing MVP Code 238 Testing with Mock Objects 239 EasyMock 240 Integration Testing with GWTTestCase 247 Testing a View 247 Testing a Ser vlet 252 Acceptance Testing with Selenium 253 A Very Simple Example 255 What Can Go Wrong? 257 Summary 257 14 Optimizing for Application Speed 259 Design Patterns for Speed 259 Caching 260 Prefetching 263 Thread Simulation 266 Bundling Data 273 Download from www.wowebook.com ptg xiv Contents Speed Measurement Tools 277 Speed Tracer 278 YSlow 280 Page Speed 283 JavaScript Debuggers 285 Summary 286 15 Deploying Your Application 287 Compilation 287 Modules 289 Code Splitting 291 Deployment 297 Working with Client-Only GWT 297 Working with Client-Plus-Server GWT 297 Summary 300 Index 301 Download from www.wowebook.com ptg Preface Developing modern, interactive, complex web sites has become a harder task since users’ expectations are higher today. The bar has been raised by the current crop of appli- cations such as Gmail or Google Maps, and developers are expected to work up to that level and provide similarly powerful new web sites. The style, speed, and interaction levels of modern sites practically rival those of classical desktop installed applications, and of course users don’t want to go back. How do you develop such sites? It can be said that the usage of Ajax was what started the trend toward such distinc- tive applications, but even given that technique, the rest of the development of web pages was the same, tools were the same, testing methods were the same, and the whole result was that the programmers’ jobs had gotten much harder than needed. (Personally, I should confess that I really never liked classic-style web development: Building large-sized applications was harder than it needed to be, JavaScript was—and still is—missing constructs geared to complex systems, the click-wait-click-wait again cycle was inevitably slow and not very interactive, and, to top it all, unless you were quite careful with your testing, your design was prone to fail on this or that browser in unexpected ways.) GWT, in just a very few years, has grown into a powerful tool by harnessing the power of Java and its considerable programming environment and many development tools, and producing efficient and consistent output, despite the too-many and well- known incompatibilities between browsers. Getting started with GWT isn’t that hard—documentation is reasonably good, the development environment can be Eclipse or several other equally powerful IDEs, and programming is quite similar to old-fashioned Java Swing coding—so you can have your first short application up and running in a short time. Creating production-quality, secure, internationally compliant, high-level code can be, however, a bit more complex. You need to take many factors into account, from the ini- tial setup of your project and development of the user interface, to the final compile and deployment of your application. Similarly, we’ll also have to focus on methodologies and on software design patterns, so we can go forth in a safer, more organized way toward the complete application. For example, we’ll consider how the model-view-presenter (MVP) pattern can not only enhance the design of the application, but also help run fully automatic tests, in modern Agile programming style, to attain higher quality, better tested software. We’ll be working with the latest tools and versions; not only GWT’s (2.0.3 just now), but also Eclipse, Subversion, Tomcat, Apache, MySQL, and so on. Because all these tools Download from www.wowebook.com ptg are open source, we can support the notion that an appropriate software stack can be built starting with GWT and ending with a full open web solution. After my earlier confession on my dislike of classic web development strategies, I should now aver that GWT did change that for me. Working in a high-level setting, with plenty of tools, and practically forgetting about browser quirks, HTML, CSS, and JavaScript, while gaining in clarity, maintainability, and performance, has made web appli- cation creation an enjoyable task again! The Structure of This Book Chapters 1 through 3 deal with the basic setup for working with GWT. After consider- ing the main reasons and objectives for using GWT, we’ll study what other tools are required for serious code development, the methodology to use, and the internal aspects of projects. Chapters 4 and 5 are the backbone for the book, for they deal with the basic design patterns that we use for building the User Interface. The code style and idioms devel- oped here will be used throughout the rest of the book. Chapters 6 and 7 deal with communications with servers, either through RPC (to connect with servlets) or through direct Ajax (to communicate with remote services). Chapters 8 and 9 study how to add both JavaScript coding and third-party APIs to your application. Together with the previous two chapters, everything that’s needed for mashing up services and getting information from different sources will have been covered. Chapters 10 and 11 have to do with common server related problems, such as security aspects, and file upload and download. Chapter 12 deals with developing GWT applications that will be used worldwide and covers both internationalization and localization. Finally, Chapters 13 through 15 consider general themes such as testing GWT appli- cations, optimizing their performance, and finally deploying them. Who Should Read This Book This book goes beyond “just learn GWT,” and is targeted to programmers who already have a basis of GWT programming and want to encompass other web applications, serv- ices, APIs, and standards as well, to produce Web 2.0-compliant Rich Internet Applications (RIAs). A previous experience with web development, possibly in a J2EE environment, will come in handy. Having read this book through, the reader should not only be able to develop a RIA on his own by just using GWT, but he will also have a reference book to help solve the common problems that arise in such applications. Complete source code is given for all examples, so getting started is quicker. xvi Preface Download from www.wowebook.com ptg Web Resources for This Book The Google Web Toolkit site at http://code.google.com/webtoolkit/ is a mandatory ref- erence, and so is the forum at http://groups.google.com/group/google-web-toolkit. The code examples for this book are available on the book’s web site at www.informit.com/title/9780321705143. xviiPreface Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg Acknowledgments Writing a book can be a daunting task (and I should know because the idea really frightened me at the beginning) and without the collaboration of many people, it would probably become almost impossible. I would like to thank the Addison-Wesley team, led by Trina MacDonald, who first had the idea for this book and then followed it through all the way, helping me deal with the many stages and norms of the book writing process, answering myriad ques- tions, and giving shape to the book from an initial basic plan to its final structure. The fact that I live “down below” in Montevideo, Uruguay, with five hours’ difference in time with regard to the location of her office, also surely added an extra bit of complexity to the whole experience! I would also like to thank Songlin Qiu, the development editor, and Jason Essington, Jim Hathaway, and Daniel Wellman, the three technical editors, who had the task of sift- ing through all my code and text, endeavoring to make the book clearer, better organ- ized, correctly formatted, well structured, and more easily understood. Reading other people’s code is never easy, and doing that with a critic’s eye, seeking to make it clearer, checking if it’s well commented and explained, and endeavoring to make the whole more pedagogic and comprehensible obviously adds a lot to the job to be done. I would also like to highlight and thank the contributions of Gabriel Ledesma, Enrique Rodríguez, Miguel Trías, and Rodolfo Vázquez, who through many discussions (with or without an eventual agreement!) on Java, design patterns, web development techniques, usability, and teaching, helped shape many of the chapters in the book. Finally, I would also like to thank the mostly nameless Google people who made GWT possible, who roam the GWT forums helping everybody in need of aid, who write documentation, examples, and tutorials, and who constantly seek to make GWT even better and more powerful. Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg About the Author Federico Kereki is a Uruguayan systems engineer, with more than twenty years’ expe- rience as a consultant, system developer, university professor, and writer. He has been applying and teaching GWT since 2007. He has taught several computer science courses at the Universidad de la República, Universidad ORT Uruguay, and the Instituto Universitario Autónomo del Sur. He has written texts for some of these courses, and several articles—on GWT and other open source topics—for magazines such as Linux Journal and LinuxPro Magazine in the United States, Linux+ and Mundo Linux in Europe, and for web sites such as linux.com and IBM Developer Works. Kereki gave talks on GWT in public conferences organized by Microsoft and TCS in 2008 and 2009, and he has used GWT to develop several companywide Internet systems for businesses in Uruguay. His current interests tend toward software quality and software engineering— with Agile Methodologies topmost—while on the practical side he is working with tools such as GWT and Java, Ajax, SOA, and PHP. He has been working with Open Source Software (FLOSS) for more than ten years, with both Windows and Linux. He resides, works, and teaches in Uruguay. Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 1 Developing Your Application Why would you use GWT? What can you develop with it and how? Before delving into specifics (as we’ll be doing in the rest of the book) let’s consider the answers to these questions, so you’ll know what to focus on. Developing applications with GWT can be seen as a straightforward job, but you should ask some interesting questions to unlock the way to powerful, distinct, applications. What kind of applications should you develop with GWT? (And, given the current push for Cloud Computing, you can even add “Where would you deploy your application?”) How can you go about it? And, why would you use GWT? Let’s consider all these questions in sequence to start you on your way through this book, knowing your goal and the road to it. Rich Internet Applications When you start reading about Rich Internet Applications (RIAs), your JAB (Jargon, Acronyms, and Buzzwords) warning should go off because there are many words that are bandied about, without necessarily a good, solid definition or a clear delimitation of their meanings. Basically, what we build are web applications that have the look and feel of classic desktop applications but that are delivered (and “installed”) over the web. Many tools have been used for this purpose, such as Java (through applets), Adobe Flash, and more recently, Microsoft Silverlight, but used in this way, all these tools are beaten, in terms of practicality, by simple HTML-based systems. The RIAs that we will be developing are based on JavaScript and Ajax and just require an appropriate browser to run. Classic web applications were developed with a different set of tools, subjected the user to frequent waits (the hourglass cursor was often seen), and had severe restrictions as to usability, with a much clunkier feel to them than desktop installed programs. Although some people distinguish between RIAs and the kind of interactive web applications we build, the frontiers are getting blurrier and blurrier. You could argue that Flash or Silverlight require preinstalled plugins, or that development runs along different Download from www.wowebook.com ptg lines, but in terms of the final result (which is what the user experiences) differences are not so marked, and well-designed HTML/JavaScript/Ajax applications can compete for equality with applications developed with the other tools. (Also, some people opine that HTML 5 can seriously challenge Flash, up to the point of making it obsolete, but that’s still to come.1) There used to be obvious differences—the ability to store local data at the user’s machine was the biggest one—but tools such as Google Gears or current developments in HTML 5 have provided this feature to web applications.2 Given its ubiquity (from desktops to netbooks, and from cell phones to tablet PCs) the browser can be considered a universal tool, and Ajax provides the best way for the creation of highly interactive applications. Of course, a few years ago there weren’t many tools for doing this (GWT itself appeared in 2006) and creating heavy-lifting interactive code with just JavaScript wasn’t (and still isn’t) an appealing idea.3 Furthermore, given that users have been subjected for many years to web applica- tions, and are familiar with their idioms, you are a bit ahead in terms of user interface design by keeping to a reasonable standard. As for the language itself, using Java as a tool—even if it gets compiled into JavaScript, as GWT does—provides both a way around JavaScript’s deficiencies and introduces a widely used language with plenty of development tools, which has been used over and over for all kinds of applications and has been proved to scale to large- sized applications.4 Web 2.0 Web 2.0 is another expression that has been bandied about a lot since its invention in 2004. Though there are way too many definitions for it, most seem to agree on the idea of using the “Web as Platform,” where all applications run in a browser instead of being preinstalled on your desktop. Furthermore, the idea of allowing users to produce their own contents (à la Wikipedia) is also included, highlighting the collaborative aspect of work, and thus bringing into the fold all kind of community and social networking sites (think Facebook or YouTube). Finally (and that’s what actually works for us) the concept of mashing together different data sources (probably from many web services) is also included. 2 Chapter 1 Developing Your Application 1. See www.ibm.com/developerworks/web/library/wa-html5webapp/ for an article of some HTML 5 features already available in current browsers. 2. Google Gears’ development was practically stopped (other than support for currently available versions) by the end of 2009 because of the upcoming HTML 5 features for local storage. 3. It might be said that developing large applications with, say, Flash, isn’t a walk in the park either, for different reasons to be sure, but complicating the programmer’s job in any case. 4. It should be remarked that GWT isn’t the only such compile-to-JavaScript solution; for example, the Python-based Pyjamas project (http://code.google.com/p/pyjamas/) provides Python-to- JavaScript translation, and there are many more similar tools. Download from www.wowebook.com ptg GWT applications can obviously be used for producing highly interactive people sites, but they can also link together information from different origins, consuming web services with no difficulty, either connecting directly to the server or by means of proxy- based solutions. Various data formats are also not a problem; if you cannot work with such standards as XML or JSON, you can include external libraries (or roll out your own) through JSNI or Java programming. (We cover this in Chapter 8, “Mixing in JavaScript,” and Chapter 9, “Adding APIs.”) In this context, the phrase Service-Oriented Architectures (SOA) frequently pops up. Instead of developing tightly integrated, almost monolithic, applications, SOA proposes basing your systems on a loosely integrated group of services. These services are general in purpose and can be used in the context of different applications—and, as previously mentioned, GWT is perfectly suited to “consuming” such services, dealing with different protocols and standards. (We’ll cover this in Chapter 6, “Communicating with Your Server,” and Chapter 7, “Communicating with Other Servers.”) If your company is cen- tered on an SOA strategy, your GWT-developed applications will fit perfectly well. Cloud Computing Next to the idea of using the browser as the basis for the user’s experience, the most current term related to modern application development is Cloud Computing. This idea reflects the concept of sharing resources over the web, on demand, instead of each user having a private, limited pool of resources. In this view, software is considered a “service” (the acronym SAAS, which stands for “Software as a Service,” is often used) and a resource similar to more “tangible” ones as hardware. (As an aside, the vulnerability of some operating systems, most notably Windows, to viruses, worms, and similar attacks, has given a push to the idea of using a simple, secure, machine and storing everything “on the web,” letting the cloud administrators deal with hackers and program infections.) For many, this concept is yet another cycle going from centralized resources (think mainframes) to distributed processing (PCs, possibly in client/server configurations) and now to having the web as your provider. The main requirements for such an architecture involve reliable services and software, delivered through specific data centers, and running on unspecified servers; for the user, the web provides an access to a cloud of resources. For GWT applications, your applications are basically destined from the ground up to be used “in the cloud” because of the standard restrictions imposed by browsers. Distributing an application over the web, accessing it from anywhere, and having your data stored in a basically unknown place are all characteristics of any applications you might write.5 3Rich Internet Applications 5. With current (or forthcoming) standards, you might also resort to storing data locally, or to using your own private, dedicated, resources, but that’s not original and more often associated with clas- sic desktop applications. Download from www.wowebook.com ptg The “Death of the Desktop” The trend toward Cloud Computing has even spawned a new concept: the “Death of the Desktop.” This presents rather starkly the problem of going overboard, to the limit: From the appearance of mini netbooks (with flash-based disks, slow processors, not much RAM) and iPhone-look-alike cell phones, some have reached the conclusion that desk- top applications (and even desktop computers!) are on their way out. If this were true, it could be great for GWT developers, but things are a bit different. Despite several impressive opinions and pronouncements from people all over the industry, the trend toward more powerful machines, with CPUs, memory, and I/O facili- ties that put to shame the supercomputers of just a few years ago, doesn’t seem to be slowing down. Even if you are enamored with the latest netbooks or high-powered cell- phones, you should accept that working all the time with minimal screens isn’t the way that things can get done at a company. (And for gaming or graphic-intense usages, small machines aren’t so hot either; they may do, however, for business-oriented applications.) In any case, GWT can help you because you can use its layout facilities and CSS styling to produce applications for just about any device out there. Also, remove the rosy glasses for an instant. Cloud computing offers several advantages (and GWT applications can be considered to be right in the middle of that concept) but also presents problems, so you need to plan accordingly. Aside from the obvious difficulty of dealing with possibly flaky web connections, security and compatibility can be stum- bling blocks. (On the other hand, scalability is well handled; there are plenty of large sites, with hundreds or thousands of servers, proving that web applications can scale well.) The important point is, with or without desktops, GWT provides some ways around these kind of problems, and we’ll study this in upcoming chapters.6 Advantages of GWT Why would you develop with GWT? Shouldn’t directly using JavaScript make more sense? How do you manage with browser quirks? Let’s consider the reasons for GWT. HTML Ubiquity and Browser Differences The first reason for GWT applications is the ubiquity of HTML. Even if some time ago browsers for, say, cell phones, weren’t as capable as their desktop brethren, nowadays you can basically find the exact same capabilities in both. In terms of GWT, this is a boon because it means that a well-designed application can run and look pretty in devices from 3 inches to 25 inches.7 4 Chapter 1 Developing Your Application 6. And, of course, these inconveniences haven’t stopped anyone from developing HTML-based applications! 7. Don’t expect to get the screen design right the first time; managing to build clear, small screen browser applications is more an art than a science. Download from www.wowebook.com ptg This availability is somehow tempered because today’s browsers are not created equal—but you certainly knew that if you designed web pages on your own! When Microsoft’s Internet Explorer ruled the roost, having practically 100% of the browser market, this wasn’t a noticeable problem. However, today browser usage statistics point to a different status quo: Mozilla Firefox and Safari, among others, have started carving larger and larger niches in the market, and in some countries (mostly European) they have out- numbered Internet Explorer. The current trend is toward applying web standards, and that bodes well for web developers. In any case, GWT is quite adept at solving browser quirks and differences, so the point may be considered moot for the time being. JavaScript Deficiencies Even assuming fully standard-compliant browsers, the fact remains that JavaScript, no matter how powerful, isn’t a good language from the specific point of view of software engineering. Because this isn’t a book on JavaScript, we won’t delve in its main prob- lems, but using it for large-sized application development can be, to say the least, a bit complicated. This language isn’t well adapted either to development by large groups of people, and the tools it provides for system development aren’t that adequate, so the programmer must add extra code to bridge the distance between a modern object-oriented design and its actual implementation. One solution that has been applied is the usage of different libraries that provide a higher-level way of using the language.8 GWT solves this problem in a radically different way, by enabling the use of the higher level Java language, for which there are plenty of modern development, testing, and documentation tools. Software Methodologies to Apply For classic application development, many well-known methodologies exist, but in the context of modern web development, you should definitely use some techniques. Classic Development Problems If you learned to develop systems years ago, you were surely exposed to the Waterfall Model or some other methodologies directly based on it. In this model for the develop- ment process, progress is seen as flowing like a waterfall from stage to stage, through 5Software Methodologies to Apply 8. You could consider Google’s “Closure” library (see http://code.google.com/closure/) used for Gmail’s development, or Yahoo!’s YUI library (see http://developer.yahoo.com/yui/), jQuery (http://jquery.com/), Dojo (www.dojotoolkit.org/), Prototype (www.prototypejs.org/), MooTools (http://mootools.net/), and many others. The functionality of these libraries isn’t always the same, but there’s considerable overlap between them, showing the problems they set out to solve are real and well known. Download from www.wowebook.com ptg well-defined phases (see Figure 1.1) starting with the Analysis of Requirements, follow- ing with the Design of the Solution and its Implementation, then to Testing (or Quality Assurance), and finally to Installation and future Maintenance. 6 Chapter 1 Developing Your Application Figure 1.1 The classic Waterfall Model isn’t the best possible for GWT development. This model is flawed in several ways (and of course, there are some fixes for that) but its main problem is its orientation to highly regimented industries such as Construction, in which late changes can be quite costly to implement, usually requiring tearing down what was done and practically starting anew. Another point—and an important one—is that you cannot expect users to be fully aware of what they require; it is sometimes said “Users don’t know what they want, but they know what they don’t want.”9 Classical methodologies do not take this into consid- eration, and might thus incur important costs, because newly discovered or determined requirements can invalidate a previous design. Finally, it’s difficult to predict where difficulties will occur; problems with functionality are usually found “on the go,” and if going back to change something to help future development is too costly, you can face a dilemma: Spend money and time revising your Analysis Design Programming Testing Installation Maintenance 9. “I’ll know it when I see it” is another way of expressing this. Download from www.wowebook.com ptg design, or keep your substandard design, and spend money and time later trying to make your software do tasks it wasn’t well designed to do. It has been said that the Waterfall Model, and similar ones, are based on the old “Measure Twice, Cut Once” saw, but you cannot actually apply this when you don’t actually know what’s being measured! (And, furthermore, what happens if requirements change along the way, and by the time you finish with development, the problem has actually changed?) Modern, agile technologies try to take this into account and work in a radically different way, and that’s the way you should use with GWT. Agile Methodologies Several software development methodologies seek to reduce the time between the requirement analysis phase and the development phase to develop at least parts of the system in shorter times, using possibly an iterative method to advance to the final appli- cation. Prototypes are frequently used to bridge the distance between the user and the developer, helping both to understand what’s actually required. Instead of attempting to do a whole system at once, development is parceled in smaller subsystems. The user is involved all the time, instead of providing his input (in the form of requirements) only at the beginning and then dealing with the system after its installation. All these suggestions are currently applied in Agile Software Methodologies (born in 2001) that emphasize collective (i.e., users plus programmers) development of systems, in highly iterative steps, with frequent verification and (if needed) adaptation of the written code. Agile Methodologies usually break a complex system into several short stages, substi- tuting short, easily measured and controlled iterations, for long-term (and hard to do) planning. Each iteration (usually shorter than a month) involves a mini development cycle that includes all the stages associated with a Waterfall Model but finishes with giv- ing the users a working product with increasing functionality that serves not only as a measure of advance, but also as an aid to determine if changes are needed. The delivered software is used as the main measurement for progress, instead of depending on a Gantt chart or other documents. GWT is perfectly suited to such methodologies, because it can offer iterative develop- ment, rapid prototyping (and here tools such as UiBinder, which we will study, can help quickly develop appropriate interfaces), and automated testing. The latter point is partic- ularly important: Given that development can (and will) go back and forth, and code used in a previous iteration can be modified several times along the complete develop- ment process, it’s important to check whether old functionality hasn’t been lost and whether bugs have been introduced. GWT has tools that provide for both unit testing (at the lowest level) and acceptance testing (at the user level). Forever Beta? As a side effect of the iterative development process, it’s usually hard to define what con- stitutes a “version” of the final system. Because practically every iteration produces new 7Software Methodologies to Apply Download from www.wowebook.com ptg functionality, and the final goal isn’t as well defined as with classic methodologies (in which the complete roadmap is laid out at the beginning and then preferably left unchanged) with iterative development, you deliver the system in many small steps, rather than in large ones. In this context, it’s not unknown for systems to be considered in “perpetual beta”; beta testing refers to the tests done by actual users with a system that is close to the full product but not necessarily complete. (An extreme case of this is Google’s Gmail, which was considered to be at beta level from 2004 to 2009!) With GWT, you can provide functionality increases in short steps, and the web model enables for easy distribution of the updated code.10 Summary We touched upon several considerations that impact web application development. In the rest of the book, we will be elaborating on them and provide specific techniques to help you develop company-sized RIAs with the expected levels of quality and functionality. 8 Chapter 1 Developing Your Application 10. This could be said, of course, of any web-based application not necessarily written with GWT; the point is that GWT helps you work this way. Download from www.wowebook.com ptg 2 Getting Started with GWT 2 Why use GWT 2? What are its advantages and disadvantages? What is required to take advantage of this tool? How should you plan your work? In this chapter we consider the whys, whats, and hows of GWT development; why you and your company should con- sider its usage, what components are included in its framework, and how—with which tools—you should do your development. Why Use GWT? Since its introduction at the JavaOne conference in May 2006, GWT has been evolving, going from version 1.0 through 1.7 and up to the current 2.0.3, but the question is still asked frequently: Why would you code web applications with GWT? Why not stay with JavaScript? What advantages does GWT bring? Is it a complete framework for web development? Even if you are already comfortable with GWT, these questions bear con- sideration: Why would you recommend using GWT at your job? Let’s start by defining what GWT is: It’s a tool that enables you to develop client-side code, working with Java, and compiling your code into JavaScript, which is then exe- cuted at the client’s browser. The final product is a web application with almost desktop- application levels of interactivity, which executes client-side with minimal needs of server-side code or interaction. Compiling into JavaScript provides an extra touch of speed, and the final code is optimized and as good, or better, than human written code. And, most important, you won’t need to (Okay, almost never will; see Chapter 4, “Working with Browsers,” for specific cases in which you may want or have to) worry about browser differences and quirks because GWT generates appropriate code for each specific browser.1 1. The idea of compiling to JavaScript isn’t a GWT exclusive: Several other tools, such as Pyjamas (see http://code.google.com/p/pyjamas/) or OpenLaszlo (see www.openlaszlo.org/) also work this way. Download from www.wowebook.com ptg Why Java? The usage of Java is quite relevant. For starters, there’s a wealth of Java-experienced pro- grammers, and the learning curve for GWT isn’t as hard as for other frameworks.2 On the other hand, JavaScript development is as yet still far from mature, with little support from IDEs, and too basic debugging methods—alert(...) calls are still probably the most commonly used tool! Of course, if Java isn’t good enough, or if you have some special-case-coding situation, you can resort to JavaScript code that can call and be called from your Java code. Another plus is debugging your code by using Java debuggers. Java also is well suited for Agile Development Methodologies, such as XP or Scrum. TDD (Test Driven Development) is highly encouraged, with support for JUnit testing, both for client- and server-side code. (In Chapter 13, “Testing Your GWT Application,” we’ll go over the topic of GWT code testing.) Web development also becomes easier; you can develop the presentation layer by either using Swing-like techniques (as in common Java desktop programming), an HTML-based approach, or the recent UIBinder declarative technique. (We will cover this ground in Chapter 5, “Programming the User Interface.”) If you want a better look, you can integrate widget or effects libraries to enhance the look of your application. Some Actual Disadvantages So, what’s not to like? To be fair, let’s consider some of the (real or imagined) disadvan- tages of GWT. Despite all we have said, which are good reasons for using GWT, you should also mind some negative points. For starters, GWT web pages aren’t indexable by search engines. Because the applica- tion is generated dynamically, search engines cannot index its contents. Some solutions, such as cloaking, exist (having two sets of pages and presenting one to common users and other with different content to search engine spiders) but they are difficult to apply with GWT and might even fall afoul of indexing engines. If your business model somehow depends on Search Engine Optimization (SEO) considerations, GWT might not be fully adequate for you. Also, GWT pages do not “gracefully degrade” in the presence of older browsers; either the application will or won’t run, but there’s no middle ground with limited func- tionality or restricted scope.3 Te c h n i q u e s s u c h a s progressive enhancement can be applied (meaning, deploy a most basic site, which enables extra functionality if and only if the browser supports it) but would demand duplicate coding, because if the user’s browser 10 Chapter 2 Getting Started with GWT 2 2. The GWT team explains that they didn’t just want to develop technology for the sake of doing so, and Java already had many available tools. See http://code.google.com/webtoolkit/ makinggwtbetter.html for more details. 3. The current attitude is “just upgrade,” which tends to ignore valid reasons why users would want or have to use older versions of more modern browsers. Download from www.wowebook.com ptg doesn’t match GWT’s requirements, the application most surely will fail. You can apply some workarounds for a few problems (such as using iframes to simulate Ajax calls) but an inappropriate browser is usually a stumbling block. Happily, this objection is slowly fading away, and in time you won’t need to worry about this. However, if you require, for example, using your application with cell phones, you might find out that many users will be locked out because of inadequacies in their browsers.4 For security, GWT applications are just as prone to attacks as any JavaScript applica- tion. (We will consider security aspects in Chapter 10, “Working with Servers.”) Using GWT won’t allow you to just ignore security. There are, however, some security-related enhancements coming up (to avoid some of the more common attacks) but for the time being, you should just take the same precautions as for web applications developed with any other tools.5 Developers may complain that compiling and deploying is slower than with straight JavaScript. This is probably trivially true (no compilation beats any compilation!) but the point here is that writing the code is slower and more bug prone with JavaScript than with Java. There are fewer tools for JavaScript coding, browsers have many quirks, and your program will be full of if (isIE8)... tests. A solution is to use libraries such as jQuery, prototype, Dojo, or ExtJS, which wrap some of those differences internally... but in this case, why not use GWT that enables you to fully forget those differences?6 Similar notions are proposed by others who simply suggest that to develop rich Internet applications, you should be directly working with JavaScript, because that’s “what real programmers do,” and forget any alternatives! This conclusion is supported by the notion that the Java-to-JavaScript conversion should necessarily be poor (because of the differences between both languages) and that the generated code will be bulky and slow. Apart from the unwarranted latter objections, the key point here is that JavaScript isn’t the only problem; the differing implementations across browsers are the other big problem. Real browser-independent code is quite hard to write (and larger, too) and it’s difficult to ensure the application of the required discipline; you end up spending more time, and writing more code, to achieve the same results as with a few lines of Java.7 11Why Use GWT? 4. Android cell phones and iPhone tend to work well out-of-the-box, but that’s not the rule for all cur- rent cell phones. 5. In any case, note that GWT applications are neither more nor less prone to attacks than any other JavaScript website, so this shouldn’t be considered a GWT-specific disadvantage but rather a “fact of life” as pertaining to web development. 6. For more on this, read the “Reveling in Constraints” article by one of GWT creators, Bruce Johnson, at http://queue.acm.org/detail.cfm?id=1572457. 7. If you worry about what happens when a new browser version is released, check the answer to “Will my app break when a new browser comes out?” in the GWT FAQ at http://code.google.com/webtoolkit/doc/latest/FAQ_GettingStarted.html. Download from www.wowebook.com ptg The GWT Components In this section we will discuss the three basic components of GWT: the high-quality Java-to-JavaScript compiler, the Java Runtime Environment (JRE) Emulation library, and the User Interface (UI) library. If you were used to previous versions of GWT (up to 1.7) you may be missing the “hosted browser” that enabled you to try out code in hosted mode, but GWT now uses “in browser development” (and development mode) that enables you to directly test your application on your own browser, as we’ll see in Chapter 3, “Understanding Projects and Development.”8 Compiler The first and most important component of GWT is the Java-to-JavaScript compiler. It takes your Java 1.5 code and produces distinct equivalent JavaScript versions that can be run on all supported browsers: At the time of writing, all versions of Safari and Firefox, Opera (at least up to versions 9.x), and versions 6 to 8 of Internet Explorer—Google Chrome, being based on the same layout engine (WebKit) as Safari, is also supported and runs Safari’s code.9 (Actually, the number of generated versions of the JavaScript code can be far larger, if your application uses i18n—internationalization—as we’ll study in Chapter 12, “Internationalization and Localization.”) Code can be minimized for size, for faster downloads; there are also facilities for code splitting, which lets you download the required JavaScript code in smaller pieces, on a when-required basis; see Chapter 15, “Deploying Your Application,” for more on this. The compiler does several code optimizing tasks during the compilation run, with the stated goal of producing high-quality code, ideally besting code developed by hand by experienced programmers. (Usually, code is obfuscated, but you can also ask for “Pretty” or even “Detailed” output to better understand what the compiler does. The desired option can be chosen when compiling, as we’ll see in Chapter 15.) Among the many optimizations applied, the following are most significant:10 n Dead Code Elimination: Code that never gets called isn’t included in the out- put file. If you develop a class with ten methods, but only use a couple of them, the compiler won’t generate code for the rest of them. Similarly, if you inherit a module with several dozen methods, output code will be generated only for the actually required methods; you won’t incur in any size penalty because of methods you don’t need. 12 Chapter 2 Getting Started with GWT 2 8. This “in browser” mode was, at least for a while, called OOPHM, standing for Out Of Process Hosted Mode. 9. There are many other browsers (some for cell phones) that are also based on WebKit and thus could run GWT applications; check http://webkit.org/ for more details. 10. See http://code.google.com/p/google-web-toolkit/wiki/AdvancedCompilerOptimizations for planned future optimizations. Download from www.wowebook.com ptg n Constant Folding: When the value of an expression can be known at compile time, the expression is calculated beforehand, and the result will be directly used. For example, if you write something such as Window.alert("Hello "+"World") the generated JavaScript code will be something such as $wnd.alert("Hello World"); note that this executes a bit faster because the needed string concatena- tion is already done. n Copy Propagation: An extension of Constant Folding, it lets you carry forward the value of a variable if it can be known at compilation time. For example, given the code int a=15; int b= a*a+5; the second line will be compiled as if it read int b=230. n String Interning: To avoid creating the same strings over and over again, each distinct string is created once (and assigned to a variable with a name such as $intern_22, for example) and used everywhere.11 n Code Inlining: For short, simple methods, GWT substitutes the actual method code for the original call. All these optimizations mean that the final code will be quite good. On the negative side, GWT won’t do partial compilations; whenever you want to compile your code, GWT looks at the whole of it and does a monolithic compilation to maximize the number of possible optimizations. This was a conscious design decision by the Google development team; you lose such advantages as reusing previously compiled modules, but you gain a greater performance. If you were to compile a piece of code in advance, you couldn’t do dead code optimization, for example, because you couldn’t predict if a cer- tain method would be required.12 There are some other snags you need to be aware of: n JavaScript doesn’t have a 64-bit integer numeric type, so GWT emulates long variables with a pair of 32-bit integers. This works properly but is noticeably slower. Also, when you use JSNI, you cannot pass these variables to JavaScript routines. n For floating point numbers, JavaScript provides only a 64-bit (double) type, which implies that overflows and result precision in arithmetic operations won’t be exactly the same as in Java. Also, the strictfp keyword is disregarded. n Exceptions are also handled differently. In JavaScript, most of the Java produced exceptions (such as NullPointerException or MemoryOverflowException) are replaced by a JavaScriptException. This causes a problem: When running in development mode, a NullPointerException will be thrown, and you need to catch (NullPointerException e) but in compiled mode, you need to catch 13The GWT Components 11. Yes, having variables start with “$” makes you think somebody in the GWT group must really miss his PHP coding days... 12. Also, note that while in “development mode,” GWT doesn’t require (or do) a complete compile/deployment process because it actually executes Java code. Download from www.wowebook.com ptg (JavaScriptException e) and you duplicate your exception handling code. Another option, of course, is just to catch (Exception e) and then check for the class of the exception. n JavaScript provides no multithreading, so all thread-related functions will either be ignored or rejected. JRE Emulation Library While in common Java you can use a prepackaged library without further concerns; because of the way the GWT compiler works, it requires access to actual source code for any class you might want to use. This requirement extends to the JRE, and GWT pro- vides a partial implementation of it called the JRE Emulation Library.13 There are only four packages: java.io (sorely restricted!), java.lang, java.sql (also quite limited), and java.util, but you can find some missing classes or methods. (This is logical: For example, because JavaScript cannot use files, most of the classes in java.io just wouldn’t work when compiled into JavaScript.) Going into details, the java.io package is most limited, including just the Serializable interface, which RPC considers a synonym for isSerializable. (We’ll get to this in Chapter 6, “Communicating with Your Server.”) The reason for this limita- tion is simple: The GWT-produced JavaScript code is executed in a browser sandbox and cannot access any local files or printers. This might change (a little) with some HTML 5 features, but for now there’s nothing you can do. More interesting, java.lang includes exceptions, classes, general utility methods, and some interfaces. Exceptions ArithmeticException IndexOutOfBoundsException ArrayIndexOutofBoundsException NegativeArraySizeException ArrayStoreException NullPointerException AssertionError NumberFormatException ClassCastException RuntimeException Error StringIndexOutOfBoundsException Exception Throwable IllegalArgumentException UnsupportedOperationException IllegalStateException Classes Boolean Character Byte Class 14 Chapter 2 Getting Started with GWT 2 13. Check http://code.google.com/webtoolkit/doc/1.6/RefJreEmulation.html for details. Download from www.wowebook.com ptg Double Object Float Short Integer String Long StringBuffer Number StringBuilder Utility: Math Systema Interfaces: Appendable Comparable CharSequence Iterable Cloneable Runnableb a. Note that system.err and system.out won’t work in web mode, unless you use the System.setErr(...) and System.setOut(...) calls. b. Because JavaScript provides no multithreading, runnable won’t run in a separate thread as in standard Java. The java.sql package includes three classes useful for date/time processing but nothing else. And of course, from a security point of view, you wouldn’t want to try to connect directly to a SQL database from your client, would you? Classes Date TimeStamp Time Finally, java.util includes the following exceptions. Exceptions ConcurrentModificationException NoSuchElementException EmptyStackException TooManyListenersException MissingResourceException Classes AbstractCollection EventObject AbstractHashMap HashMap AbstractList HashSet AbstractMapEntry IdentityHashMap AbstractMap LinkedHashMap AbstractQueue LinkedHashSet 15The GWT Components Download from www.wowebook.com ptg Classes AbstractSequentialList LinkedList AbstractSet MapEntryImpl ArrayList PriorityQueue Arrays Stack Collections TreeMap Date TreeSet EnumMap Vector EnumSet Interfaces Collection Map Comparator Queue Enumeration RandomAccess EventListener Set Iterator SortedMap ListIterator SortedSet List You can also look for certain GWT packages that provide extra functionality that Java programmers take for granted: n com.google.gwt.i18n.client.DateTimeFormat and com.google.gwt.i18n.client.NumberFormat provide formatting functions. n com.google.gwt.core.client.Duration can be used for timing purposes. (See Chapter 14, “Optimizing for Application Speed,” for more on benchmarking and performance aspects.) The returned values are double, so performance is better than with the long Timer. (See the discussion at the end of the previous section about long emulation in GWT.) n com.google.gwt.user.client.Random provides a substitute for java.util.Random. n com.google.gwt.user.client.Timer can be used instead of java.util.Timer. As a general advice, before relying on any specific class or exception, check whether it’s actually implemented, or just a placeholder needed for JRE compatibility, or a trimmed down, limited, version of the usual JRE version. (You need to check GWT’s own source code to do this check; yes, not very simple or friendly…) On the other hand, don’t think that Java programming will become near to impossi- ble with GWT. As we saw, in some cases there are alternative classes, and in others, you can usually get by with JavaScript (JSNI) or any open source library. 16 Chapter 2 Getting Started with GWT 2 Download from www.wowebook.com ptg UI Library GWT provides a large, standard set of widgets (such as buttons or text input fields) and panels. Using widgets is quite similar to Swing, so Java programmers can feel at home; however, note that there are no layout managers (discussed next) and panels or CSS are used instead for positioning objects. Widgets are usually mapped into browser objects (think Heavyweight objects in Swing) so they’ll share the visual aspect of whatever browser the user adopts. Styling can be done on an object-per-object basis, or more generically by applying CSS, which is the preferred solution. Some composite objects, more often associated with rich desktop applications, are also included, such as a DatePicker for date input, SuggestBox for real-time suggestions based on whatever the user has typed, RichTextArea for format- ted text input, and more.14 Panels are containers for widgets or other panels. Panels also do double-duty as layout managers; for example, FlowPanel uses standard HTML flow rules (or Swing’s FlowLayout’s), whereas VerticalPanel stacks its elements vertically. We’ll go into more detail about creating the user interface in Chapter 5. Setting Up GWT To develop a basic GWT application, you can make do with just about any text editor and a few command line utilities, but for more serious work you need several other tools. (And as we saw, the Google developers thought that a good reason for using Java was the quantity of available tools for that language, so why skimp?) In this section, we’ll consider several tools and plugins you should use for better GWT development. Writing Code Though you can develop GWT applications with just a text editor, Java, and a few scripts, you should get Eclipse (at www.eclipse.org/), which is the Google-suggested IDE for GWT. You should go for the JEE version, the most complete version for Java development. All the examples in this book were developed with Eclipse 3.5, Galileo. You can also give NetBeans (at http://netbeans.org/) with Gwt4nb (see https://gwt4nb.dev.java.net/) a try, or go for Intellij IDEA (at www.jetbrains.com/idea/). I know programmers who swear by each of these alternatives, so take your pick! If you go with Eclipse, the Google Plugin for Eclipse (at http://code.google.com/ eclipse/) is practically mandatory; functions you usually had to do with shell commands (such as creating a new project) can now be done within Eclipse. (See Figure 2.1.) Installation is the same as for any plugin: Open Eclipse, go to Help, Install New Software, add the Google Plugin URL, and it downloads and installs the rest of GWT. 17Setting Up GWT 14. See http://gwt.google.com/samples/Showcase/Showcase.html for samples of most available widgets and panels. Download from www.wowebook.com ptg (We’ll go over the usage of the plugin in Chapter 3.) The plugin also provides other fea- tures; for example, it can help you work with UiBinder (as we’ll be doing in Chapter 5), or with JSNI (as in Chapter 8). 18 Chapter 2 Getting Started with GWT 2 Figure 2.1 The Google Plugin for Eclipse is a must, and it simplifies creating both common web and Google App Engine applications. (As an aside, Cypal Studio for GWT [at http://code.google.com/p/cypal-studio/] was an alternative to the Google plugin, but for GWT 2, it’s in alpha version just now. As an extra advantage, it simplified creating remote services [we’ll get to this in Chapter 6] and deploying your application [see Chapter 15], but in its current alpha status, I wouldn’t recommend it and suggest waiting for a release version.) Lastly, all developers should follow the same standards. CheckStyle (athttp:// checkstyle.sourceforge.net/) is a tool that enforces whatever rules you decide to follow; by default, Sun’s Eclipse-CS (at http://eclipse-cs.sourceforge.net) is a suitable plugin for Eclipse; after installing it the standard way, a new option will be added to your project menu (CheckStyle), and after running it, all nonstandard lines will be marked. Download from www.wowebook.com ptg Version Control Management For version control management, I work with Subversion; therefore, I suggest using Subclipse (at http://subclipse.tigris.org/), which is an Eclipse plugin, currently at version 1.6.5. Installation is similar to the Google Plugin’s; to be on the safe side, pick all pack- ages and let Eclipse request any missing packages. I have also worked with Subversive (at http://community.polarion.com/), another Eclipse plugin, and had no problems; pick whichever suits you best.15 As for Subversion servers, which is beyond this book, but you can either install your own server (see http://subversion.tigris.org/ for details) or use any of several public free or paid servers; google a bit for this. (An appropriate venue could be Google’s own Project Hosting at http://code.google.com/hosting/) Testing One of GWT’s greatest advantages is testing, and you have to install JUnit (from www.junit.org/). The latest version, currently 4.8, can possibly be installed directly through your distribution package manager (that was the case with OpenSUSE) or by following the installation instructions at http://junit.sourceforge.net/README.html#Installation. You need to add JUnit4 to the list of libraries; right-click on your project, click Properties, Java Build Path, Libraries, and add JUnit4. For testing coverage metrics, add EclEmma (at www.eclemma.org/). This plugin adds a new launch mode (coverage) which, after running your test suite, produces a marked up listing of your source code showing, which lines were or weren’t exercised by the test. (See Chapter 13 for more on testing.) Installation is the usual one for Eclipse plugins. Finally, for unit testing, EasyMock (at http://easymock.org/) is a valuable tool. Added to the GUI patterns that we apply (see Chapter 5 for a discussion of the MVP design pattern as applied to GWT) it will simplify writing our automatic tests. You need to install both EasyMock (currently at version 2.5.2) and the EasyMock Class Extension (at version 2.4), and add both jars to the build path. See Figure 2.2 on the next page for a finished installation. Running and Deploying You should also have Firefox (at www.mozilla.com/en-US/). Actually, about any browser could do, but Firefox has lots of great plugins for development, such as FireBug (a debug- ger and inspector) and FireCookie (an extension that lets you examine cookies).16 19Setting Up GWT 15. Of course, version control is part of all development projects, and not really GWT-specific, but I wanted to include everything that you would be likely to require for serious application development. 16. If you are running Linux, ironically you won’t be able to use Google’s own Chrome for develop- ment, since a required plugin isn’t expected to be available until at least version 5 of the browser. Download from www.wowebook.com ptg 20 Chapter 2 Getting Started with GWT 2 Figure 2.2 A nicely filled out set of libraries for development and testing When you start developing with GWT and testing your applications with your own browser (we’ll get started with this in Chapter 3) you will be required to install an appropriate plugin; follow onscreen instructions, depending on what browser you use. Also, you should also get Selenium (at http://seleniumhq.org/), which is a great tool for acceptance tests, and the Selenium IDE (a good help for setting up the tests) is provided as a Firefox extension. To finish, to deploy the actual application, you need some servlet container (such as Tomcat, Jetty, or Glassfish, among many possibilities) or if your server side isn’t Java based, a web server (Apache or Lighttpd come to mind). We won’t be covering how to install and set up these programs in this book. Summary We analyzed why you should use GWT (and even some reasons against it, which aren’t that weighty, in our opinion), what GWT is in terms of its components, and which tools you need to get the most out of your development. Now, let’s get started with actual GWT development, from the initial setup to the final deployment of your application. Download from www.wowebook.com ptg 3 Understanding Projects and Development In this chapter we’ll create a project, study its structure, configure its modules, and show how development works with GWT. Since GWT 1.0, this whole process has evolved significantly; now it’s more streamlined, with a plugin for easier project creation and OOPHM for faster, simpler development and testing. Creating a Project First, let’s start by creating a project—a “Hello World” application if you will—though we won’t use it to showcase GWT, but rather to study the structure of a project. (And we will throw some criticism at this simple application in Chapter 5, “Programming the User Interface.”) We won’t do any coding because GWT can generate such code by itself, and it’s good enough for our purposes. And, by the way, the simplest way to create your own project and make certain that it was created correctly is by deleting Google’s standard code and start writing your own. You can create a GWT project in at least three ways: by means of the Google Plugin for Eclipse, by using a shell script, or even directly by hand, file per file—though of course there isn’t much going for the latter option, so we’ll avoid it.1 Using the Google Plugin for Eclipse Originally, GWT provided some scripts to create a project with all required files and directories (and we’ll look at this next) but using the Google Plugin for Eclipse (which we installed in Chapter 2, “Getting Started with GWT 2”) is by far the simplest way. 1. In Eclipse, go to File, New, Other, Google, Web Application Project. 2. Give the project a name. (I inspiredly chose sampleproject.) 1. For just a single example of other ways to create a project, Maven users could utilize the CodeHaus plugin at http://mojo.codehaus.org/gwt-maven-plugin/, and it’s likely that sooner or later you’ll find plugins for just about any development environment. Download from www.wowebook.com ptg 3. Specify which package should be created. (I went with com.fkereki.sample.) 4. Check Use Google Web Toolkit and Use Default SDK. It is possible to install sev- eral versions of the SDK at the same time; for example, for testing purposes, or for building GWT projects created with older versions. 5. Because we aren’t going to deploy this project to Google App Engine, uncheck Use Google App Engine. 6. Click Finish. That’s all there is to creating a project with the Google Plugin for Eclipse; a certainly simple process. Using the GWT Shell Script If you aren’t using Eclipse, you can use the webAppCreator shell script to generate all needed directories and files. Note that before GWT 1.6 you had to use two scripts, projectCreator and applicationCreator, to accomplish the same result. You need to specify the module name, and you can also include several parameters: n -overwrite means all existing files will be overwritten. n -ignore means existing files will be left as-is and not overwritten. Note that -ignore and -overwrite are mutually exclusive; you cannot specify them both. n -out someDirectory specifies the output directory; by default, the current one. n -XnoEclipse implies no Eclipse-specific files will be created; you would use this if you plan to use other IDE instead. n -XonlyEclipse on the contrary means the script will generate only those files needed for Eclipse; you can import this project into Eclipse. The following is the (slightly abridged for legibility) result of a project creation run: > cd work > md secondsample > sh webAppCreator com.kereki.secondsample Created directory /home/fkereki/work/src Created directory /home/fkereki/work/war Created directory /home/fkereki/work/war/WEB-INF Created directory /home/fkereki/work/war/WEB-INF/lib Created directory /home/fkereki/work/src/com/kereki Created directory /home/fkereki/work/src/com/kereki/client Created directory /home/fkereki/work/src/com/kereki/server Created file /home/fkereki/work/src/com/kereki/secondsample.gwt.xml Created file /home/fkereki/work/war/secondsample.html Created file /home/fkereki/work/war/secondsample.css Created file /home/fkereki/work/war/WEB-INF/web.xml Created file /home/fkereki/work/src/com/kereki/client/secondsample.java 22 Chapter 3 Understanding Projects and Development Download from www.wowebook.com ptg Created file /... /work/src/com/kereki/client/GreetingService.java Created file /... /work/src/com/kereki/client/GreetingServiceAsync.java Created file /... /work/src/com/kereki/server/GreetingServiceImpl.java Created file /home/fkereki/work/build.xml Created file /home/fkereki/work/README.txt Created file /home/fkereki/work/.project Created file /home/fkereki/work/.classpath Created file /home/fkereki/work/secondsample.launch Created file /home/fkereki/work/war/WEB-INF/lib/gwt-servlet.jar After creating the project, you can import it into Eclipse with these steps:2 1. Go to File, Import, General, Existing Projects into Workspace. 2. Browse to the directory with the new project and select it. 3. Uncheck Copy Projects into Workspace, so Eclipse uses the directory you chose. 4. Click Finish. We’ll study the files layout in the next section. Project Structure Let’s now get into the project structure. (See Figure 3.1 on the next page.) You need several directories: n Your production Java code goes in the src directory, which is further divided into client (code that runs at the user’s browser), shared (a post-GWT-2.0 addition for code used both at the client and the server) and server (code that runs server- side).3 You can further create any subpackages within these three directories. You can have other directories for client-side code but need to include them with the element. On the other hand, server-side code must reside within server; you cannot specify other directories for it. If you want to share classes in client and server-side code, you should include them in the shared directory, because they need translation into JavaScript; this automatically implies that all client-side code limitations apply to those classes. n For testing, you may have test and gwttest directories (for JUnit and GWTTestCase automatic tests) as we see in Chapter 13, “Testing Your GWT Application.”4 23Project Structure 2. An equally valid alternative would be importing it into Netbeans and working with the GWT4NB plugin, as we mentioned in Chapter 1, “Developing Your Application.” 3. Note that in standard Java fashion, com.kereki.sample.client actually stands for the com/kereki/sample/client subdirectory. 4. The testing directories are actually optional but skipping automatic tests would go against the idea of GWT development. Download from www.wowebook.com ptg Figure 3.1 The basic structure for a recently created project. This structure is missing the directories for your automatic test code. n Your output code will be produced in the war folder. This directory is in the appropriate format for Java web servers such as Tomcat or Jetty, so you can directly deploy your application. (We see more on this in Chapter 15, “Deploying Your Application.”) Within it, you can find the files that form the client-side application (static ones such as CSS or HTML, plus the compiler-generated JavaScript files) and the Jar files and servlet configuration files for your server-side code. The basic units in GWT are modules. You use modules both for the actual client-side application and for libraries that you want to reuse across several projects. The module definition goes in the project root and has a gwt.xml extension. A most basic module description for our recently created project could contain 24 Chapter 3 Understanding Projects and Development Download from www.wowebook.com ptg Let’s first examine this example and then move on to a fuller description of available elements and attributes. n The optional rename-to attribute in the element lets you change the generated application name from com.kereki.sample.client to the far friend- lier, simpler sampleproject. n The elements include the contents of other modules; in this case, we import basic GWT functionality (com.google.gwt.user.User) and a default style for widgets (com.google.gwt.user.theme.standard.Standard). n The element shows the starting class for the application. n The element defines which directories will or won’t be included for code generation; here, we just include the standard client directory. For a simple project, you don’t need more than this, but several elements let you add further capabilities to your project; let’s now go into more detail. n The root element for the gwt.xml file is . You can define only a single module per gwt.xml file, but you can have several differently named modules within the same project. This would allow having, for example, a production mod- ule definition (used for deployment, as we see in Chapter 15) and a development module definition, which could be compiled more quickly, just for a single browser and language. n Yo u c a n u s e t h e < rename-to> attribute to rename a module (as we previously did) to give the compiled application a simpler name; otherwise, instead of going to http://yourwebsite.com/sample, the user would have to browse to http:// yourwebsite.com/com.fkereki.sample.Sample, which isn’t so friendly. If you were having a production module and a development module as previously described, you could use this attribute so both modules produce an identically named application. For example, we could have development.gwt.xml, whose compilation would just produce a Safari version of the code; we’ll see more of this in Chapter 15. ...same as earlier... n The element lets you include (the default action) or exclude specific directories and file patterns. The default source path is client and not including any source elements is equivalent to just including . You can also include or exclude files or patterns; see the following ele- ment description. 25Project Structure Download from www.wowebook.com ptg n The element lets you specify an entry point class for your application (i.e., a class that implements EntryPoint) as in . If you have several entry point classes, their onModuleLoad methods will be executed sequentially, in the same order as in the module file. n The An (even better from the point of view of packaging everything together) alternative would be adding . Then, we can write a JSNI animateAllLinks(...) function that will get all links on the current window ($('a') is a selector, the jQuery way of referring to all links, and we already saw the need for $wnd) and make them grow to 150% size when the mouse goes over them, and reduce them back to normal size when the mouse moves out. (Yes, I know that the effect isn’t that pretty, but on the other hand, it’s simple to code!) The stop(...) method cancels any previous animation enabling a new one to proceed. The code is then private static native void animateAllLinks() /*-{ $wnd.$('a').hover( function() { // mouse in animation code $wnd.$(this).stop().animate({fontSize:'150%'}, 250); }, function() { // mouse out animation code $wnd.$(this).stop().animate({fontSize:'100%'}, 250); }); }-*/; If you call the animateAllLinks(...) function, jQuery will add the two anima- tions to every link it can find. Of course, you can do much better, by both doing animations and CSS styling, but that would be beyond this book; we just want to see how to use an external JavaScript library, and JSNI enabled us to do it quite simply. A Steampunk Display Widget Do you remember Nixie tubes? The earliest calculators (in the 70s before LED times) used them for displays. Basically, those tubes included several numeral-shaped cathodes, 143JSNI 8. If you want to test any implementation, there are many “test suites” such as the one at http://home.claranet.de/xyzzy/src/md5.cmd that you can use. Download from www.wowebook.com ptg which glowed orange when power was applied to them.9 Hobbyists are nowadays using these tubes to build Steampunk-styled appliances like clocks; let’s build a widget enabling us to display numbers, as in Figure 8.1.10 144 Chapter 8 Mixing in JavaScript 9. See www.tube-tester.com/sites/nixie/different/nixie-tube-links.htm for many links to Nixie tubes information. 10. I remember my father bringing home a desk calculator—Monroe or Sweda brand, I think—which used such tubes for its display… does this memory date me? Figure 8.1 A Nixie display widget, showing vital information such as James Bond’s secret agent number, Charles Darwin and Abraham Lincoln’s shared birthday, and all the available digits. Cˇestmír Hýbl provides, in his web site, both a useful routine and the needed images (see http://cestmir.freeside.sk/projects/dhtml-nixie-display/) with which we can build a Java NixieDisplay class. package com.kereki.nixietest.client; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.ui.HTMLPanel; public class NixieDisplay extends HTMLPanel { JavaScriptObject display; public NixieDisplay( String pName, int pDigits, String pAlign) { super("
"); display = initNixieDisplay(pName, pDigits, pAlign); } Download from www.wowebook.com ptg private native JavaScriptObject initNixieDisplay( String pName, int pDigits, String pAlign) /*-{ var nd = new $wnd.NixieDisplay(); nd.id = pName; nd.charCount = pDigits; nd.charWidth = 30; nd.charHeight = 50; nd.charGapWidth = 0; nd.urlCharsetImage= "nixielib/zm1080_d1_09bdm_30x50_8b.png"; nd.align = pAlign; return (nd); }-*/; public native void setText(String pText) /*-{ this.@com.kereki.nixietest.client.NixieDisplay::display.init(); this.@com.kereki.nixietest.client.NixieDisplay::display.setText(pText); }-*/; } Before getting into details, let’s see how this class would be used. The following code produces the display that was shown earlier. NixieDisplay display1 = new NixieDisplay("nd1", 5, "right"); NixieDisplay display2 = new NixieDisplay("nd2", 10, "left"); NixieDisplay display3 = new NixieDisplay("nd3", 12, "right"); FlexTable ft = new FlexTable(); ft.setWidget(0, 0, new Label("James Bond is:")); ft.setWidget(0, 1, display1); ft.setWidget(1, 0, new Label("Darwin/Lincoln Birthdate:")); ft.setWidget(1, 1, display2); ft.setWidget(2, 0, new Label("All digits:")); ft.setWidget(2, 1, display3); RootPanel.get().add(ft); display1.setText("007 "); display2.setText("02-12-1809"); display3.setText("-123456.7890"); The JavaScript routine needs a
(with an id) to display the characters, so extending HTMLPanel was a sensible choice. The constructor requires a name (for the
), a length (in digits) and a default alignment ("left" or "right") for the text. To set a value, the setText(...) method is provided. The logic for constructing 145JSNI Download from www.wowebook.com ptg and updating a display were taken from the web site; in particular note the way of get- ting at an attribute of a Java object from JavaScript (which in this case happens to be a function) in the setText(...) method, with this.@com.kereki.nixietest.client .NixieDisplay::display.init(). Okay, maybe this kind of widget isn’t your “cup of tea,” but if you like this retro look, check out Hýbl’s site and code, and you’ll learn how to use other tubes, or create a dynamic old-style clock or calculator for your web application. Using other JavaScript widget libraries is similar to the job we did here, in any case, and you can enhance your GWT applications this way. JSON JSON (JavaScript Object Notation) is an alternative to XML, with the extra advantage that it is quite easily processed by JavaScript.11 GWT has always provided support for this protocol, and in particular GWT 2 includes several classes and methods that make JSON processing even easier. Let’s create a simple news reader, using a JSON data source, to examine how to get and process such data. We use Yahoo’s search services12 that searches for news and pro- vides them in JSON format. (You can also get the feed in JSONP format, which makes for even easier processing; we’ll touch on this next.) A sample call to http://search.yahooapis.com/NewsSearchService/V1/newsSearch?appid= YahooDemo&query=computer&results=5&language=en&output=json provides a JSON string, with up to five news in English language that include the word “computer.” With some added blanks for readability, and abridging some texts, the output might be as fol- lows: {"ResultSet": { "totalResultsAvailable":"19978", "totalResultsReturned":5, "firstResultPosition":"1", "Result":[ { "Title":"Micro computer club meets Jan 12 in Hanover Twp.", "Summary":"HANOVER TWP. - The Micro Computer Club will meet ...", "Url":"http:\/\/recordernewspapers.com\/articles\/2010\/01\/09...", "ClickUrl":"http:\/\/recordernewspapers.com\/...", "NewsSource":"Hanover Eagle", "NewsSourceUrl":"http:\/\/www.recordernewspapers.com...", 146 Chapter 8 Mixing in JavaScript 11. See www.json.org/ for more information on JSON, including parsers and generators for several dozen languages and environments. 12. See http://developer.yahoo.com/search/web/V1/webSearch.html for a full specification of query strings. Download from www.wowebook.com ptg "Language":"en", "PublishDate":"1263082654", "ModificationDate":"1263082655"}, { "Title":"The Hindu Business Line...", "Summary":"Over one lakh government schools...", ...}, {...}, {...} ]} } The JSON notation (a subset of JavaScript) is easy to read: Braces surround objects, brackets surround arrays, and attributes are followed by a colon and their values. This news server provides a ResultSet object, with three attributes (totalResultsAvailable, totalResultsReturned, and firstResultPosition) having to do with how many and which items were returned, and another attribute (Result) that is an array of objects itself, each one representing a news item. These objects have several attributes, including Title, Summary, URL, NewsSource, Language, among others. (An aside, just for completeness: If you need to produce JSON code with client-side code—not a likely situation, because JSON is usually produced at the server and con- sumed at the client—you can use the JsonUtils.escapeValue(...) method to pro- duce escaped, valid JSON strings, and a simple logic to build the output string piece by piece, in a similar way to what we did in Chapter 7, “Communicating with Other Servers,” to produce XML output.) The view for our reader will be simple: a TextBox for specifying what terms you want to search, a Button to do the call, and an HTMLPanel to display an appropriately built up string, with the news titles, summaries, and clickable links to the original web page. See Figure 8.2 for our simple form design. 147JSON Figure 8.2 A simple news reader that will let us search for news on any search terms we want. Download from www.wowebook.com ptg After a search for ABRAHAM LINCOLN you could get something such as Figure 8.3, showing a maximum of five news items. (This parameter can be changed.) Clicking on a title would open a new page with the original news web site. 148 Chapter 8 Mixing in JavaScript Figure 8.3 The results of searching for news regarding ABRAHAM LINCOLN, with a preset maximum of five news items. The NewsReaderView.ui.xml and NewsReaderView files are quite simple, involv- ing just a few fields and a single callback (when the button gets clicked). The ui.xml file is simply:

NewsReader

Search for:
The corresponding view is package com.fkereki.mvpproject.client.newsReader; // ...several imports... Download from www.wowebook.com ptg public class NewsReaderView extends View implements NewsReaderDisplay { @UiTemplate("NewsReaderView.ui.xml") interface Binder extends UiBinder { } private static final Binder binder = GWT.create(Binder.class); @UiField TextBox searchFor; @UiField Button getNews; @UiField HTML newsResult; SimpleCallback onGetNewsCallback; public NewsReaderView() { super(); HTMLPanel dlp = binder.createAndBindUi(this); initWidget(dlp); } @Override public String getTextToSearchFor() { return searchFor.getValue(); } @Override public void setNews(String htmlNews) { newsResult.setHTML(htmlNews); } @Override public void setOnGetNewsCallback(SimpleCallback acb) { onGetNewsCallback = acb; } @UiHandler("getNews") void uiOnGetNewsClick(ClickEvent event) { onGetNewsCallback.onSuccess(null); } } The required NewsReaderDisplay interface is also quite simple, with methods for getting the textbox contents, setting the HTML list of news, and setting the button call- back; let’s examine it just for completeness: 149JSON Download from www.wowebook.com ptg public interface NewsReaderDisplay extends Display { String getTextToSearchFor(); void setNews(String htmlNews); void setOnGetNewsCallback(SimpleCallback acb); } Now we can get to the more interesting part, and consider the NewsReaderPresenter .java logic. The main part hinges on creating the “Get News” button callback, that will construct the correct URL and connect to the news search service to get the latest news items. package com.fkereki.mvpproject.client.newsReader; // ...imports... public class NewsReaderPresenter extends Presenter { public static String PLACE = "newsReader"; public NewsReaderPresenter( final String params, final NewsReaderDisplay newsReaderDisplay, final Environment environment) { super(params, newsReaderDisplay, environment); getDisplay().setOnGetNewsCallback(new SimpleCallback() { @Override public void goBack(final Object result) { getNewsViaXhr(); } }); } void displayNews(final NewsFeed data) { // format and show the news... } void getNewsViaXhr() { final String newsUrl = "http://search.yahooapis.com"; final String newsPath = "NewsSearchService/V1/newsSearch"; final String newsParams = "appid=YahooDemo&query=" + URL.encode(getDisplay().getTextToSearchFor()) + "&results=5&language=en&output=json"; 150 Chapter 8 Mixing in JavaScript Download from www.wowebook.com ptg final XhrProxyAsync xhrProxy = getEnvironment().getModel() .getRemoteXhrProxy(); xhrProxy.getFromUrl(newsUrl, newsPath, newsParams, new AsyncCallback() { @Override public void onFailure(final Throwable caught) { // warn about the error... } @Override public void onSuccess(final String result) { final NewsFeed data = JsonUtils.unsafeEval(result); displayNews(data); } }); } } If the Ajax call succeeds, we’ll use the JsonUtils.unsafeEval(...) method— basically just a plain call to JavaScript’s own eval(...) function, with no further safety measures; thus, the “unsafe” part of the name—to produce a NewsFeed object, an overlay for the underlying JavaScript object; let’s study this a bit. How do you work with a JavaScript object with Java code? You could go for the older JSONParser methods and build an object item per item, but it wouldn’t be so efficient as using an overlay object that will encapsulate all accesses, hiding the underly- ing JavaScript object. First, you should remember that you cannot create such an object by using Java’s new(...) syntax; the whole idea of overlays is to graft an access to an already existing JavaScript object. Because we use unsafeEval(...) to get the JavaScript version of the news feed object, we are well on our way.13 Our NewsFeed overlay class will just provide four methods, because we are only interested in a few fields of the complete JSON result: We want to know how many news items there were, and their titles, summaries, and URLs. Note the protected con- structor, that won’t enable you to even try to construct a NewsFeed object with Java. package com.fkereki.mvpproject.client.newsReader; import com.google.gwt.core.client.JavaScriptObject; 151JSON 13. This function is quite new in GWT; in fact, most online documentation shows how to accomplish the evaluation by means of a JavaScript method that directly calls eval(...). By using this func- tion you get the same result but keep to Java code. Download from www.wowebook.com ptg public class NewsFeed extends JavaScriptObject { protected NewsFeed() { } public final native String getClickUrl(final int i) /*-{ return this.ResultSet.Result[i].ClickUrl; }-*/; public final native String getSummary(final int i) /*-{ return this.ResultSet.Result[i].Summary; }-*/; public final native String getTitle(final int i) /*-{ return this.ResultSet.Result[i].Title; }-*/; public final native int getTotalResultsReturned() /*-{ return this.ResultSet.totalResultsReturned; }-*/; } All methods must be final or private, so the compiler will resolve the call at com- pile time and generate optimized, possibly inlined, code. (We’ll see the results of this optimization next.) Through overlays, your Java code can interact with the JavaScript object with no fuss. You could even add extra “Java-only” methods to NewsFeed; for example, we could write something like—and feel free to fill in the details—the getAge(...) method, which would tell how old is a piece of news. public final long getAge(final int i) { // use a JSNI method to get the PublishDate attribute // of the i-th news item, and store it to newsTimeStamp // get the current timestamp by using new Date().getTime() // and store it to currentTimeStamp return currentTimeStamp - newsTimeStamp; } Note that this enables having a richer view of the underlying JavaScript object than the original object would have enabled. Given the preceding class, the displayNews(...) method iterates through all news items (their quantity is obtained through the getTotalResultsReturned(...) method) and constructs a link (by using getClickUrl(...) and getTitle(...)) with the following getSummary(...) results. A couple of empty lines separate different news items. 152 Chapter 8 Mixing in JavaScript Download from www.wowebook.com ptg void displayNews(NewsFeed data) { String show = ""; final int news = data.getTotalResultsReturned(); for (int i = 0; i < news; i++) { show += "" + data.getTitle(i) + "
" + data.getSummary(i) + "

" + data.ResultSet.Result[i].Title + '<\/a>
' + data.ResultSet.Result[i].Summary + '

node to your current page and then set its URL appropriately) but GWT 2 added the JsonpRequestBuilder class, which enables you to do JSONP calls in a fashion similar to Ajax calls. You must, however, add to your gwt.xml module file, to use JSONP. We can modify our news reader by providing a different method, that will replace getNewsViaXhr(...). void getNewsViaJsonp() { final String newsUrl = "http://search.yahooapis.com/" + "NewsSearchService/V1/newsSearch?appid=YahooDemo&query=" + URL.encode(getDisplay().getTextToSearchFor()) + "&results=5&language=en&output=json"; final JsonpRequestBuilder jsonp = new JsonpRequestBuilder(); jsonp.requestObject(newsUrl, new AsyncCallback() { @Override public void onFailure(final Throwable caught) { // ...warn about the problem... } 154 Chapter 8 Mixing in JavaScript Download from www.wowebook.com ptg @Override public void onSuccess(final NewsFeed result) { displayNews(result); } }); The code is quite similar, but note that the AsyncCallback for the JSONP call is defined to return a NewsFeed object and that you don’t need to parse the result of the call, which is already converted into the appropriate format. Also observe that the URL we need to provide is exactly the same as in the Ajax version; the &callback=... attribute will be added by JsonpRequestBuilder. (And, should you require a different name for it—say, _call—you would have to set it via jsonp.setCallbackParam( "_call"). The default name for the JSONP callback routine is, as you might expect, callback.) Because many important, well-known Internet companies (including Yahoo!, Google, Twitter, Flicker, and more) provide JSONP feeds, this method will enable us to produce Web 2.0-like mashup services, without recurring to proxies or anything else, for an extra bit of speed. Summary Directly coding in JavaScript by means of JSNI enables you to go beyond any possible limitations that you might find in GWT. Though obviously requiring a more careful approach (and somewhat complicated rules for Java/JavaScript interaction) JSNI is widely used by the GWT developers and is a worthy tool for you to learn and use—and we will keep working with it in the following chapter, using JSNI to interact with several important APIs. 155Summary Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 9 Adding APIs There are many JavaScript libraries and web services, and you can include their features in your application by adding JSNI (as we saw in Chapter 8, “Mixing in JavaScript,”) and some GWT coding. In this chapter we see some examples and get to process data feeds to get weather information, use charts to provide a visual dashboard, and interact with geographic data working with maps. Creating highly interactive, modern web applications doesn’t mean reinventing the wheel for each specific purpose. Rather, we like to utilize the many available APIs for just about anything you might want. (Of course, you should remember that you won’t be getting the usual GWT benefits as to code optimization, dead code removal, and more, if you just produce a wrapper around a JavaScript library. If there’s a GWT-only solution, you’ll probably be better off using it.) In this chapter we see how a mixture of Java and JavaScript coding can let you easily mix in several APIs into your application, with no particular complications. We won’t restrict ourselves to Google-only routines, so you can get experience on handling APIs that might have not even been prepared for GWT. A Weather Vane Let’s start by using some libraries to get weather information, which we could include in any site.1 For my example, I wanted to get Montevideo, Uruguay (my birth place, and where I live) weather information—without merely looking out of my window! Getting Weather Data First, I needed a web service that would provide weather information. Out of the many available possibilities, I opted (no particular reason) for Yahoo! Weather RSS Feed (see 1. And yes, Weather Vane may not be a precise or correct name but is flashier sounding than Weather Widget! Download from www.wowebook.com ptg http://developer.yahoo.com/weather/). To use it, I had to go to Yahoo’s Weather site (at http://weather.yahoo.com/) and using the search function, let me learn that the WOEID2 code for Montevideo was 468052. I got that directly from examining the URL for Montevideo’s weather, which was http://weather.yahoo.com/uruguay/montevideo/ montevideo-468052/. (Another example would be 2442047, which means Los Angeles, California, USA.) Given that code, accessing the feed at http://weather.yahooapis.com/ forecastrss?w=468052&u=c would get Montevideo’s (w=468052) weather information in Celsius (u=c) degrees. If you want to consider other possibilities for getting weather information, you could check GeoNames’ site (at www.geonames.org/) and in particular its web services (at www.geonames.org/export/JSON-webservices.html) that can provide JSON weather information. For example, knowing that Montevideo is at latitude –34.858 and longi- tude –56.171 (I googled for that information; mind having it in decimal format rather than in the more classic 34°51'29"S, 56°10'14"W style) I could then access http:// ws.geonames.org/findNearByWeatherJSON?lat=-34.858&lng=-56.171 and get the (slightly edited and abridged) JSON result listed here.3 {"weatherObservation":{ "clouds":"scattered clouds", "weatherCondition":"in vicinity: rain", "observation":"SUAA 162100Z 15003KT 9999 VCRA SCT023 BKN040 23/18 Q1010", "windDirection":150, "ICAO":"SUAA", "elevation":54, "countryCode":"UY", "lng":-56.25, "temperature":"23", "dewPoint":"18", "windSpeed":"03", "humidity":73, "stationName":"Melilla", "datetime":"2010-01-16 21:00:00", "lat":-34.7666666666667, "hectoPascAltimeter":1010 } } Just so you can see GWT’s flexibility, you could also use several other RSS weather feeds, such as Accuweather’s (see http://rss.accuweather.com/rss/liveweather_rss.asp?metric= 1&locCode=SAM|UY|UY010|MONTEVIDEO) or The Weather Channel’s (see 158 Chapter 9 Adding APIs 2. WOEID stands for Where On Earth ID and is a 32-bit number used by Yahoo! for its geographic GeoPlanet services and data; see http://developer.yahoo.com/geo/ for more on this. 3. If you are curious, ICAO stands for International Civil Aviation Organization, and SUAA is the code for the Ángel S. Adami Civil Airport in Melilla, near Montevideo. Download from www.wowebook.com ptg http://rss.weather.com/weather/rss/local/UYXX0006?cm_ven=LWO&cm_cat= rss&par=LWO_rss) among many.4 Yet another possibility would be using Yahoo Pipes (http://pipes.yahoo.com/pipes/) and build yourself an appropriate JSON service. Yahoo Pipes lets you mash different feeds (such as the RSS feeds I mentioned) and produce results in JSON, XML, and more formats. Getting the Feed Now, how do we get this feed? Of course we could do a proxy (as we saw in Chapter 7, “Communicating with Other Servers,”) and extract the weather information from the RSS XML result, but the Google Ajax Feed API (see http://code.google.com/apis/ ajaxfeeds/) will help us. (Remember you cannot directly get the feed because of the SOP restrictions we studied in Chapter 6, “Communicating with Your Server.”) You can use this API to download any public data RSS/Atom feed by using just JavaScript; Google provides the necessary server-side proxy, and integrating a feed becomes easier. Also, this library can return the feed data in either JSON (by default) or XML; because we are working with client-side code, the first option is a natural for us. The (slightly edited and abridged) string we’d be getting is {"feed":{ "title":"Yahoo! Weather - Montevideo, UY", "link":"http://us.rd.yahoo.com/...", "author":"", "description":"Yahoo! Weather for Montevideo, UY", "type":"rss20", "entries":[{ "title":"Conditions for Montevideo, UY at 12:00 pm UYT", "link":"http://us.rd.yahoo.com/...", "author":"", "publishedDate":"Thu, 14 Jan 2010 12:00:00 -0800", "contentSnippet":"\nCurrent Conditions:\nFair, 26 C\nForecast:\nThu - Sunny. High: 26 Low: 17\nFri - Mostly Sunny. High: 28 Low: 21\n\nFull Forecast at ...", "content":"\u003cimg src\u003d\"http://l.yimg.com/a/i/us/we/52/34.gif\ "\u003e\u003cbr\u003e\n\u003cb\u003eCurrent Conditions:\u003c/b\u003e\u003cbr\u003e\nFair, 26 C\u003cbr\u003e\n\u003cbr\u003e\u003cb\u003e Forecast:\u003c/b\u003e\u003cbr\u003e\nThu - Sunny. High: 26 Low: 17\u003cbr\u003e\nFri - Mostly Sunny. High: 28 Low: 21...", "categories":[] }] } } Finally, how do we get to use this API? We have to load it into memory and then interact with it to get the data. The standard way is by using JavaScript but we can go one better. 159A Weather Vane 4. Be careful, however; different codes stand for the same cities, and of course, data formats vary. Download from www.wowebook.com ptg ...rest of your site... We can use the GWT AjaxLoader API (see http://code.google.com/docreader/#p= gwt-google-apis&s=gwt-google-apis&t=AjaxLoaderGettingStarted) to load the Feed API, and then this API to get the RSS weather information, transformed into JSON… sounds harder than it is, in fact! By the way, loading the JavaScript library on demand helps providing a faster startup time, along the lines of RunAsync(...), which we’ll use in Chapter 15, “Deploying Your Application.” Also note that after the library has been loaded, future load requests won’t reload it, so performance will be very good. Getting Everything Together Let’s go at this step by step. To use the GWT AjaxLoader API, you have to add to your gwt.xml file. You also have to get the gwt-ajaxloader-1.0.0.tar.gz file from Google’s down- load site (at http://code.google.com/p/gwt-google-apis/downloads/list), extract the gwt-ajaxloader.jar file from it, and add it to your project’s client-side code build path. (Note that Google provides several APIs that simplify using services, without having to write JavaScript by yourself. In any case, in this chapter we use both such APIs and JSNI-based methods to consider all possible solutions.) First, you need to initialize AjaxLoader and then use it to load the Feeds library into memory; you’ll provide the library’s name ("feeds") and the version you want to use ("1" at the time, but there can be new ones in the making). You also have to provide a Runnable object, whose run(...) method will be used as a callback, after the API is 160 Chapter 9 Adding APIs Download from www.wowebook.com ptg loaded. Code like the following should be used to initially load the JavaScript library and then to actually get the feed. AjaxLoader.init(); final AjaxLoaderOptions options = AjaxLoaderOptions.newInstance(); AjaxLoader.loadApi("feeds", "1", new Runnable() { public void run() { getFeed(); } }, options); Getting at the Feed Data with an Overlay We’ll get to the getFeed(...) routine soon, but let’s first see how we’ll process the feed. The easiest way to get the JSON data is through an overlay, as we saw in Chapter 8. public class WeatherFeed extends JavaScriptObject { protected WeatherFeed() { } public final native String getFeedDescription() /*-{ return this.feed.description; }-*/; public final native String getItemContent() /*-{ return this.feed.entries[0].content; }-*/; public final native String getItemLink() /*-{ return this.feed.entries[0].link; }-*/; public final native String getItemTitle() /*-{ return this.feed.entries[0].title; }-*/; } The GWT method that uses the WeatherFeed data could be as simple as the following: void processWeather(final WeatherFeed ww) { final VerticalPanel vp = new VerticalPanel(); vp.add(new Anchor(ww.getFeedDescription(), ww.getItemLink())); vp.add(new HTMLPanel(ww.getItemContent())); RootPanel.get().add(vp); Window.alert("Check it out!"); } 161A Weather Vane Download from www.wowebook.com ptg Getting the Feed with JSNI Now, let’s get back to actually getting the data. A suitable getFeed(...) routine requires JSNI; a possible solution is private native void getFeed() /*-{ var myself= this; var url= "http://weather.yahooapis.com/forecastrss?w=468052&u=c"; var feed= new $wnd.google.feeds.Feed(url); feed.load(function(result) { if (!result.error) { myself.@com.kereki.apisdemo.client.Apisdemo::processWeather (Lcom/kereki/apisdemo/client/WeatherFeed;)(result); }}); }-*/; Notice the usage of $wnd to get at the google.feeds variable, and also the call to the Java processWeather(...) method, with the usual JSNI style we saw in Chapter 8. An important detail: you might think of writing that call as this.@com... instead of declaring and using the myself variable as given, but that would be an error because this would point to the recently created function object and not to yours.5 Dashboard Visualizations For Management Information Systems (MIS) applications, adding a dashboard showing the most important business indicators is a good way to provide a bird’s eye glance to the current situation of your company. Instead of showing plain numbers, graphics and charts are usually chosen, and we’ll now turn to several ways of providing such visualizations. The easiest way would probably be using the Google Visualization API (at http:// code.google.com/docreader/#p=gwt-google-apis&s=gwt-google-apis&t=Visualization), which provides access to all kinds of graphs, both static (just an image) and animated (meaning you can click on chart items and fire events, for example).6 An example of the usage of this API (we’ll get to details later) is shown in Figure 9.1.7 162 Chapter 9 Adding APIs 5. Bone up on JavaScript closures if you are not sure about this. A possible reference is https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Closures. 6. This API also provides a Table object (why, yes, a table is also a visualization tool, isn’t it?) whose data can be sorted, paged, and formatted; check it out at http://code.google.com/apis/visualization/ documentation/gallery/table.html. 7. By the way, the data I used for the examples were taken from GeoHive (at www.xist.org/); other possibilities (among many available) would be The CIA World Factbook (https://www.cia.gov/library/ publications/the-world-factbook/) or NationMaster (www.nationmaster.com/). Download from www.wowebook.com ptg Figure 9.1 Some sample visualizations of basic world demographic data, provided by the Google Visualizations API. You can pick several other packages. Google provides the Google Chart API (at http://code.google.com/apis/chart/), which can be used with JSNI, or directly by pro- viding an appropriate URL; for example, by linking to http://chart.apis.google.com/ chart?chs=480x360&cht=bhs&chtt=World%20Population&chd=s:dZHFEEDDDD9&ch xl=0:|Rest|Japan|Russia|Nigeria|Bangladesh|Pakistan|Brazil|Indonesia|USA|India| China|1:|Population|2:|Countries|&chxt=y,x,t you can provide an image as shown in Figure 9.2, but note that you’ll have to do some coding to convert numbers (the popula- tions, in this case) to letters (see the chd parameter in the URL above); not hard, anyway. 163Dashboard Visualizations Figure 9.2 The Google API Chart has a rather complex interface but enables you to produce charts by just linking to a specific URL. Download from www.wowebook.com ptg Several more JavaScript or Flash-based libraries can help producing visualizations such as the ones in this chapter. Personally, I’d rather go for JavaScript than for Flash (after hav- ing said, in Chapter 1, “Developing Your Application,” that using GWT was advantageous because you didn’t require any plugins, it wouldn’t do to actually recommend a Flash- based visualization library, would it?) but via JSNI you can interact with both of them. With all libraries you might find that given chart styles might not run or be shown on all browsers; a nice solution to this is to apply the deferred binding techniques we used in Chapter 4, “Working with Browsers,” and either provide an alternative chart style or at least give an adequate warning. Using the Google Visualization API Using this API requires adding to your gwt.xml file. You won’t have to use the GWT AjaxLoader API (as in the weather feed example) because the VisualizationUtils package already provides the necessary function; for example, you could load the API and set it up for displaying PieChart and AreaChart objects with the following line of code:8 VisualizationUtils.loadVisualizationApi(onVisualizationsLoadCallback, PieChart.PACKAGE, AreaChart.PACKAGE, Gauge.PACKAGE); The onVisualizationsLoadCallback method will be called as soon as the API is loaded in memory and ready to be used; you can use it, for example, to initialize the visualizations for your page. (In Chapter 15 we’ll see how we could split the code away, so it would actually be loaded only if needed.) final Runnable onVisualizationsLoadCallback = new Runnable() { public void run() { final HorizontalPanel hp = new HorizontalPanel(); final PieChart worldPopPie = new PieChart(create2010PopTable(), create2010PopOptions()); hp.add(worldPopPie); final AreaChart popGrowthChart = new AreaChart( createPopGrowthTable(), createPopGrowthOptions()); hp.add(popGrowthChart); final Gauge popGauge = new Gauge(createPopGaugeTable(), createPopGaugeOptions()); hp.add(popGauge); RootPanel.get().add(hp); } }; 164 Chapter 9 Adding APIs 8. All Google provided visualizations include a PACKAGE String that identifies them. Download from www.wowebook.com ptg Each visualization requires a data table and visualization options. Data tables can have many columns; you’ll have to check what’s appropriate for the type of visualization you are creating. For example, the world population pie chart uses a two-column data table, with the country names in the first column and populations in the second. private void addIdentValueRow( final DataTable data, final String ident, final double value) { data.addRow(); data.setValue(data.getNumberOfRows() - 1, 0, ident); data.setValue(data.getNumberOfRows() - 1, 1, value); } private AbstractDataTable create2010PopTable() { /* * 2010 Population Data taken from * http://www.xist.org/earth/population1.aspx */ final DataTable data = DataTable.create(); data.addColumn(ColumnType.STRING, "Country"); data.addColumn(ColumnType.NUMBER, "Population (millions)"); addIdentValueRow(data, "China", 1338.6); addIdentValueRow(data, "India", 1166.1); addIdentValueRow(data, "USA", 307.2); addIdentValueRow(data, "Indonesia", 240.2); addIdentValueRow(data, "Brazil", 198.7); addIdentValueRow(data, "Pakistan", 176.2); addIdentValueRow(data, "Bangladesh", 156.1); addIdentValueRow(data, "Nigeria", 149.2); addIdentValueRow(data, "Russia", 140.0); addIdentValueRow(data, "Japan", 127.1); addIdentValueRow(data, "Rest of the World", 2834.1); return data; } The required options for each visualization vary a bit. Also, if you have more than one type of visualization on your application, you have to qualify which Options class you want because all are named the same way. The world population pie had the following code; note the PieChart.Options specification.9 165Dashboard Visualizations 9. If you cannot find a wrapper function to set a desired parameter, you can use the generic setOption(...) call: options.setOption("is3D", true) is the same as options.set3D(true), for example. Check online for the names of the necessary parameters. Download from www.wowebook.com ptg private PieChart.Options create2010PopOptions() { final PieChart.Options options = PieChart.Options.create(); options.setWidth(400); options.setHeight(360); options.set3D(true); options.setTitle("World Pop (millions)"); return options; } Just for completeness, we can have the code required for the other two visualizations: an area chart showing the growth and estimated sizes of the world’s population (a line chart would have done as well) and a gauge showing the current population.10 private Gauge.Options createPopGaugeOptions() { final Gauge.Options options = Gauge.Options.create(); options.setWidth(300); options.setHeight(300); options.setGaugeRange(0, 10000); options.setGreenRange(1500, 3000); options.setYellowRange(3000, 5000); options.setRedRange(5000, 10000); options.setMajorTicks(new String[] {"0", "2bn", "4bn", "6bn", "8bn", "10bn" }); options.setMinorTicks(10); return options; } private AbstractDataTable createPopGaugeTable() { /* * 2010 Population Data taken from * http://www.xist.org/earth/population1.aspx */ final DataTable data = DataTable.create(); data.addColumn(ColumnType.STRING, "Population"); data.addColumn(ColumnType.NUMBER, "Millions"); addIdentValueRow(data, "World", 6833.5); return data; } 166 Chapter 9 Adding APIs 10. There is no surefire standard for the optimum world population, but several papers seem to agree that between 1.5 and 2 billion would be best, up to 5 could be acceptable, and that the cur- rent numbers are already too big. Anyway, do not read any intended political statements in this; I just needed some data that could logically be shown in a gauge! Download from www.wowebook.com ptg private AreaChart.Options createPopGrowthOptions() { final AreaChart.Options options = AreaChart.Options.create(); options.setWidth(400); options.setHeight(360); options.setTitle("Population (millions) Growth"); options.setLegend(LegendPosition.NONE); options.setMin(0); options.setEnableTooltip(true); return options; } private AbstractDataTable createPopGrowthTable() { /* * World (actual and projected) Population Data from * http://www.xist.org/earth/his_proj.aspx */ final DataTable data = DataTable.create(); data.addColumn(ColumnType.STRING, "Decade"); data.addColumn(ColumnType.NUMBER, "Pop (millions)"); addIdentValueRow(data, "1950", 2255.9); addIdentValueRow(data, "1960", 3041.6); addIdentValueRow(data, "1970", 3711.8); addIdentValueRow(data, "1980", 4452.8); addIdentValueRow(data, "1990", 5282.4); addIdentValueRow(data, "2000", 6084.9); addIdentValueRow(data, "2010", 6866.9); addIdentValueRow(data, "2020", 7659.3); addIdentValueRow(data, "2030", 8373.1); addIdentValueRow(data, "2040", 9003.2); addIdentValueRow(data, "2050", 9539.0); return data; } Handling Events You can also process events related to visualizations, such as mouseOver, select, and more. For example, say we want to let the user click on a pie wedge and then do some- thing related to the picked country. Just after creating the object, we should add an appropriate handler to the pie chart by writing code such as worldPopPie.addSelectHandler(new SelectHandler() { @Override public void onSelect(final SelectEvent event) { final JsArray selections = worldPopPie .getSelections(); final int chosenRow = selections.get(0).getRow(); 167Dashboard Visualizations Download from www.wowebook.com ptg Window.alert("you clicked on country #" + (chosenRow + 1)); } }); The getSelections(...) method returns all clicked selections, and because on pie charts you can only click on a single wedge, by writing selections.get(0).getRow() we learn which row of the data table corresponds to the selected wedge. Of course, you would probably do something more meaningful than just letting the user know on which country he did click on! Working with Maps As a final example, let’s work on building a widget that will allow us to enter or modify the geographic coordinates of a given point. Currently, there are at least three major map APIs: Google Maps (at http:// code.google.com/apis/maps/), Microsoft Bing Maps (at www.microsoft.com/maps/) and Yahoo! Maps (at http://developer.yahoo.com/maps/). Just for variety, let’s go with the latter, which will require us a bit of XML processing and JSNI (unlike Google Maps, because we could use the GWT API at http://code.google.com/docreader/#p=gwt- google-apis&s=gwt-google-apis&t=MapsGettingStarted), and will enable us to both get interactive (clickable, draggable, and so on) maps and fixed maps (plain images) for less demanding applications. Interactive Maps The most interesting maps are interactive, meaning you can drag them around, zoom in or out, and pinpoint specific points of interest. We’ll write a simple application that will show a map with a marker on it (arbitrarily set at the American Museum of Natural History, or AMNH; my favorite place to see in New York City!) and allow you to click on other position. The map will start centered at the AMNH, and each time you click on a different spot, the marker will move there and the map will be recentered. You’ll have a continuously updated display of the latitude and longitude of the marker. See Figure 9.3 for a view of our application. We shall work in the MVP style we have been using. The Display interface for this form will be quite simple—and for an actual application, you’d probably add more methods—and will just include a couple of getters (to gain access to the current coordi- nates of the marker) and a setter (to put the marker at a given position). package com.fkereki.mvpproject.client.map1; // ...imports... public interface MapDisplay extends Display { double getLatitude(); 168 Chapter 9 Adding APIs Download from www.wowebook.com ptg Figure 9.3 Our interactive map, showing the American Museum of National History in New York City. You can reposition the marker by clicking on any spot. The map can be dragged and zoomed, too. © 2010 NAVTEQ. All rights reserved. ©2010 Yahoo! Inc. YAHOO! and the YAHOO! logo are registered trademarks of Yahoo! Inc. double getLongitude(); void setCoordinates(double latitude, double longitude); } The corresponding Presenter is trivial. We use PLACE as before to set up the menu and program the corresponding actions. package com.fkereki.mvpproject.client.map1; // ...imports... public class MapPresenter extends Presenter { public static String PLACE = "map"; 169Working with Maps Download from www.wowebook.com ptg public MapPresenter( final String params, final MapDisplay mapDisplay, final Environment environment) { super(params, mapDisplay, environment); } } The interesting logic lies at the View. Because it is responsible for creating the widg- ets, all the interaction with the Yahoo! Maps API will go here. You have to add the line to your main HTML file, so the required API will be included. To use it, you’ll also have to get a key of your own; check the “How do I get started?” section at http://developer.yahoo.com/maps/rest/V1/ for more details on that. The Yahoo! Maps API specifies you must provide a
where the map will be shown; we’ll use a HTML object for this, with appropriate contents. We’ll also define sev- eral widgets for the Latitude and Longitude fields, plus a VerticalPanel and a FlexTable to organize everything. Finally, yahooMap will point to the actual map and be a JavaScriptObject, defined and used only in JavaScript coding. package com.fkereki.mvpproject.client.map1; // ...imports... public class MapView extends View implements MapDisplay { final VerticalPanel vp = new VerticalPanel(); final HTML div = new HTML( "
"); final FlexTable ft = new FlexTable(); final TextBox lat = new TextBox(); final TextBox lon = new TextBox(); JavaScriptObject yahooMap = null; /* * AMNH= American Museum of Natural History, NYC */ final double AMNHlat = 40.780411; final double AMNHlon = -73.974037; final String AMNHDescription = "American Museum
of Natural History"; public MapView() { super(); vp.add(new InlineHTML("

Interactive Map

")); 170 Chapter 9 Adding APIs Download from www.wowebook.com ptg ft.setWidget(0, 0, new Label("Latitude:")); ft.setWidget(0, 1, lat); ft.setWidget(1, 0, new Label("Longitude:")); ft.setWidget(1, 1, lon); vp.add(div); vp.add(ft); initWidget(vp); } The getLatitude(...), getLongitude(...) and setCoordinates(...) meth- ods are quite simple, and mainly access the corresponding TextBox fields. @Override public final double getLatitude() { return Double.parseDouble(lat.getValue()); } @Override public final double getLongitude() { return Double.parseDouble(lon.getValue()); } @Override public final void setCoordinates( final double latitude, final double longitude) { lat.setValue("" + latitude); lon.setValue("" + longitude); } Now, how and when should we initialize the map? We are just defining a Composite widget here, and it won’t get displayed until later, so we cannot do any Maps API calls right now. Because we need the map to be initialized as soon as the form is shown, an easy way to achieve this is by redefining the onAttach(...) method, and our initializa- tion code will be called the moment the widget is shown.11 @Override public final void onAttach() { super.onAttach(); yahooMapInit(); setCoordinates(AMNHlat, AMNHlon); yahooMapDisplay(AMNHlat, AMNHlon, AMNHDescription); } 171Working with Maps 11. The onLoad(...) method could also be used. Download from www.wowebook.com ptg The most interesting parts come now. Initialization requires calling the YMap function to produce a map, setting its type to whatever we want (we are going for a regular map; other options include satellite and hybrid versions), and saving it to the yahooMap attri - bute we defined earlier. Note the usage of the usual JSNI $wnd prefix, and the rather long way for accessing yahooMap. You could get fancier and add several types of controls if you want; check the API documentation for that. private final native void yahooMapInit() /*-{ var map = new $wnd.YMap($doc.getElementById('myveryownmap')); map.setMapType($wnd.YAHOO_MAP_REG); this.@com.fkereki.mvpproject.client.map1.MapView::yahooMap= map; // // You can add controls: // // map.addTypeControl(); // map.addPanControl(); // map.addZoomLong(); // map.addZoomShort(); // }-*/ ; Displaying the map at a given coordinate requires creating a YGeoPoint object and using the drawZoomAndCenter(...) method. (Notice, once again, that we need a myself variable because of closure matters in the moveMarker(...) method; see the following code.) After, we can add a marker at the center point of the map, give it an “auto expand” text and show it. Finally, we must capture clicks on the map to reposition the marker; the moveMarker(...) method achieves that: The second parameter stands for the coordi- nates of the clicked point. Then, we can reset the marker coordinates with setYGeoPoint(...) and pan the view so the map will be centered by using panToLatLon(...). Finally, we can call the Java setCoordinates(...) method so the updated coordinates will be shown onscreen. private final native void yahooMapDisplay( final double lat, final double lon, final String text) /*-{ var myself= this; var map= myself.@com.fkereki.mvpproject.client.map1.MapView::yahooMap; var currentGeoPoint = new $wnd.YGeoPoint(lat, lon); map.drawZoomAndCenter(currentGeoPoint, 3); map.addMarker(currentGeoPoint,"myveryownmarker"); map.getMarkerObject("myveryownmarker").addAutoExpand(text); 172 Chapter 9 Adding APIs Download from www.wowebook.com ptg map.getMarkerObject("myveryownmarker").openAutoExpand(); $wnd.YEvent.Capture(map, $wnd.EventsList.MouseUp, moveMarker); function moveMarker(_e, _c) { map.getMarkerObject("myveryownmarker").setYGeoPoint(_c); map.panToLatLon(_c); myself.@com.fkereki.mvpproject.client.map1.MapView:: setCoordinates(DD)(_c.Lat, _c.Lon); } // // If needed, you could get the current marker coordinates by writing: // // var myobj= map.getMarkerObject("myveryownmarker"); // alert("Coords: "+myobj.YGeoPoint.Lat+", "+myobj.YGeoPoint.Lon); // }-*/ ; } I have used similar logic for applications that worked with geographical data, so the data entry personnel could check whether a store was correctly positioned, and of course, reposition it if needed. You can also use other functions that let you search for a place by giving, say, its street address; check on that for extra flexibility. Fixed Maps If you don’t need interactive maps and can do with just a image file, Yahoo! Maps also provides a REST API (see http://developer.yahoo.com/maps/rest/V1/) that you can use in a two-step process to get an image built by Yahoo!’s servers. First, you need to call a service (at http://local.yahooapis.com/MapsService/V1/ mapImage) whose answer will be an XML file with a URL in it; you can then assign the URL to an object, and the generated map will be displayed; let’s do it first by hand, and then with GWT code. For example, say we want to get an image of the map around the American Museum of Natural History, as in the previous section. We must first connect to http://local.yahooapis.com/MapsService/V1/mapImage?appid= ...yourKeyGoesHere...&latitude=40.780411&longitude=-73.974037 and the returned value will be something like the following (slightly abridged and edited) XML string. http://gws.maps.yahoo.com/mapimage?MAPDATA=...encodedMapDataGoesHere... &mvt=m&cltype=onnetwork&.intl=us&appid=...yourKeyGoesHere... &oper=&_proxy=ydn,xml Displaying that URL would provide an image such as shown in Figure 9.4. 173Working with Maps Download from www.wowebook.com ptg Figure 9.4 Yahoo! Maps also provides a two-step process to get a fixed map image. © 2010 NAVTEQ. All rights reserved. ©2010 Yahoo! Inc. YAHOO! and the YAHOO! logo are registered trademarks of Yahoo! Inc. Now, with the tools we have already been using in previous chapters, getting and dis- playing such an image becomes not too complicated. The logic that would get the URL for the map including the American Museum of Natural History (AMNH) would be along the lines of the following; let’s assume this code will go in a Presenter, and that the corresponding View will have a method enabling the Presenter to set the map’s URL. final String YAHOOID = "...yourKeyGoesHere..."; final double AMNHlat = 40.780411; final double AMNHlon = -73.974037; xhrProxy.getFromUrl("http://local.yahooapis.com", "MapsService/V1/mapImage", "appid=" + YAHOOID + "&latitude=" + AMNHlat + "&longitude=" + AMNHlon, new AsyncCallback() { @Override public void onFailure(final Throwable caught) { environment.showAlert("Couldn't connect to Yahoo Maps"); } 174 Chapter 9 Adding APIs Download from www.wowebook.com ptg @Override public void onSuccess(final String result) { final Document xmlDoc = XMLParser.parse(result); final Element root = xmlDoc.getDocumentElement(); XMLParser.removeWhitespace(xmlDoc); final String actualUrl = root.getFirstChild() .getNodeValue(); // set the View's map image URL to actualUrl } }); We use the same xhrProxy object as earlier (see Chapter 7). Note that the parsed XML object consists only of the root element, with a text node, so we can get the URL by just doing root.getFirstChild().getNodeValue(); much easier than in other examples we’ve already seen! In the onSuccess(...) method, you would finish by using the obtained actualUrl value, setting the view’s image URL to it. Check the documentation, for there are many more options than the few we used here; for example, instead of using latitude and longitude, you can specify a location by combining several of street, city, state, ZIP, or a free text location description (such as “Albany, NY”). You can also get either a PNG or GIF file, define the map’s dimensions, and the zoom level (from 1, meaning street level, to 12, meaning country level). Note that if all you care for is an image, this sample code shows a simple way of get- ting it, by building on our previous work. Getting a map requires more work than get- ting a chart from the Google Chart API (where, as we saw, providing the URL is enough to get the image) but it isn’t such a complex method either. Summary We have seen how to interact with several popular APIs, in some cases by using just Java code, and in others by mixing in JSNI. GWT applications (or, more generally, Ajax applications) usually mash up information from several places, or use interesting APIs and widgets to provide a nicer experience to the user, and this chapter has shown several ways to do that. 175Summary Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 10 Working with Ser vers All Internet applications have security concerns, and your GWT application won’t be able to elude them. In this chapter we consider important security terms and methods, and then go on to applying them for safe (or, more precisely, safer) communications with your server. The Challenges to Meet We cannot study possible solutions to security problems without having an understand- ing of the situations we’ll face, so let’s start by considering what are our objectives (or what do we mean by security), which tools we may apply, and how to use them with GWT. This chapter will differ a bit from the rest of the chapters in the book, inasmuch we’ll be showing code fragments, which implement the described ideas, rather than whole applications; fortunately, applying the methods shown isn’t that hard! Before Going Any Further Lest you end with wrong hopes, let’s start with a dire warning: Unless you use secure (meaning, SSL communications: https:// instead of http://) there is no way to com- pletely defend your application against a wise hacker with appropriate tools.1 In usual “cryptospeak,” we talk of two people, Alice and Bob, trying to communicate with each other, possibly over unsafe or unsecure channels of communication. Also, we usually consider the possible existence of several unsavory characters that might want to interfere, such as Eve (an eavesdropper, who wants to see what information is sent back and forth) or Mallory (a more malicious person, who goes beyond mere curiosity, and may even add, modify, or delete packets and programs, or redirect your communications to other servers). When you use https:// you can be certain you are connecting to the server you want, and due to the encryption of the point-to-point “tunnel” between your machine 1. This solution also has some other problems. For example, code loaded via https:// isn’t cached, so end-user performance won’t be as good when he comes back to your application. Download from www.wowebook.com ptg and the server, nobody can “listen” to the communications between your application and your services. If you don’t use this protocol, a hacker might look at the packets that flow between client and server, inspect them, and even modify them at will. He might even modify the JavaScript code you download to your machine, so it will do whatever he wants, unknown to you! We are going to discuss several ideas that can help against lesser adversaries (for exam- ple, the usage of cryptography so Eve cannot read the communications) but remember: Unless you go for full security with SSL, you cannot rest assured that your application won’t be hijacked, or your data modified, and that a determined Mallory won’t be able to harm you. Security Usually, “security” is recognized as equal to the acronym AAA, which stands for Authentication, Authorization, and Accounting, with the following meanings: n Authentication: The system should recognize a valid user. n Authorization: The system should enable specific actions only to certain users. n Accounting: The system should provide a log of used resources, performed tasks, and so on. Some other meanings are usually added, such as n Availability: Systems should be ready for use, and perform correctly and acceptably. n Confidentiality: Data should be available only to the people who should access it. n Integrity: Data is changed only in allowed ways by allowed people. n Non Repudiation: Users shouldn’t perform an action and later deny having per- formed it. For the purposes of this chapter, we’ll be mainly dealing with Authentication (so only a given set of users will be able to use the application), Confidentiality (so Eve, our eavesdropper, cannot get to the data), and Integrity (so Mallory cannot change, inject, or delete any kind of data updates). Also, when data is signed (and because the signature depends among other things on your password, nobody else could fake it) we are provid- ing a basic Non Repudiation scheme, but that’s beyond our intent. As to Authorization, there are many ways of doing this. Notice, however, that you do have to worry about server-side authorization; never assume that client-side checks are valid, and always consider that the user may be executing tampered-with code (or might have done some tampering himself!) so whichever checks or tests you need to do, must absolutely be done on the server. Finally, there are many Accounting solutions (also a server-side problem), and Availability actually hasn’t much to do with GWT.2 178 Chapter 10 Working with Servers 2. See http://code.google.com/webtoolkit/articles/security_for_gwt_applications.html for a more general description of security problems and solutions. Download from www.wowebook.com ptg Ajax Problems Although “classic” web applications usually remember the state of the application at the server side, Ajax (and thus, GWT) applications tend to do that on the client, to better take advantage of JavaScript code. However, this might tempt a developer to implement security controls at the client side, which is totally insecure. An attacker could modify the code running on his PC while testing for exploitable vulnerabilities, and your server would be receiving data from a tainted source. So, it’s worth repeating: Security controls must be totally implemented, or at least rechecked, on the server. As a consequence, never assume that any data or commands received server-side are valid. You should certainly run checks and do validations at the client to provide a more fluid experience to the end user (and in fact we saw such a pattern, PreValidation, in Chapter 6, “Communicating with Your Server”) but that won’t enable you to skip any controls at the server.3 Cryptography Cryptography has several uses, and we will apply both encryption and hashing. The first term means transforming a plain text into an unreadable crypto text that can be trans- formed back into the original only by using an appropriate key, and thus provides for security against Eve, whom we met earlier in this chapter. The second term, hashing, refers to a way of producing a fixed-size digest from a given text, in such a way that any changes to the text imply changes in the digest. If you are given a text and a digest, if they do not match, you can be sure there’s been some tampering with the data, whereas if there’s a match, it’s highly likely the text hasn’t been modified.4 Before we go any further, don’t become tempted with the idea of producing your own super-duper-ultra-highly obfuscated cryptographic method; published standard methods (such as AES, RSA, and many more; just google for “Cryptography,” and you’ll get plenty of references) have withstood analysis, checks, verifications, and attacks, and it’s unlikely any method thrown together in a short while can endure the same kind of tests. Also, do not ever rely on “Security through Obscurity,” assuming that the would-be attackers won’t guess what you did. In particular, never assume that GWT’s code obfus- cation will be enough to protect your code; a determined programmer will be able to deduce your algorithms and methods, and you’ll be left wide open to all kinds of attacks. 179Cryptography 3. Note that this doesn’t imply coding everything twice; you can easily share tests between client- and server-side code, because everything is written in Java. 4. The standard reference for Cryptography is Bruce Schneier’s “Applied Cryptography”; check it at www.schneier.com/book-applied.html, but also read his “Practical Cryptography” (check www.schneier.com/book-practical.html) for real-life practical considerations before you plunge for- ward applying methods right and left. Download from www.wowebook.com ptg Hashing This said and done, let’s start with hashing, because we have already seen the MD5 method in Chapter 8, “Mixing in JavaScript.” In that case, our implementation used JSNI; now we need a Java version for the server-side services, and we are going with the JCA (Java Cryptography Architecture)5 so we can simply write the following short method. Note that MD5 requires a zero-padded 32 bytes long hash, and some published versions of this code omit the final while in our code, thus possibly producing an (erro- neous) shorter hash. public static String md5(final String text) { String hashword = null; try { final MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(text.getBytes()); final BigInteger hash = new BigInteger(1, md5.digest()); hashword = hash.toString(16); } catch (final NoSuchAlgorithmException nsae) { } while (hashword.length() < 32) { hashword = "0" + hashword; } return hashword; } The preceding code is somewhat cavalier about errors; should a NoSuchAlgorithm - Exception be thrown, hashword would be null, and the reference to hashword .length() would then throw a NullPointerException; not very clear, and not very good programming style either! Let’s set up a Security package, with all the methods we’ll need; in fact, we’ll have dif- ferent implementations of this package, for client- and server-side coding, but with the same methods. This can be considered an application of the Façade design pattern; even if the implementations are different (as in MD5), having the same methods makes for easier coding (only one API to learn) and for testing (the same tests we use for client-side cod- ing can be used server-side.) Encrypting Back to cryptography, there are many usable methods, and we are going to use a simple —and fast—one, called RC4 (or also ARCFOUR), which is a symmetric (meaning the same key is used for coding and decoding) algorithm. RC4 is quite efficient and is applied for SSL (secure communications) and WEP. Using RC4 in server-side code is easy, but for client-side coding there isn’t such a standard implementation as JCA’s. 180 Chapter 10 Working with Servers 5. Read more on JCA at http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/ CryptoSpec.html. Download from www.wowebook.com ptg package com.fkereki.mvpproject.client; import com.google.gwt.user.client.Random; private final byte sbox[] = new byte[256]; private int i; private int j; // Set up the internal parameters (sbox, i, j) so we can // start decoding right away public void setUp(final String key) { int k; byte x; for (i = 0; i < 256; i++) { sbox[i] = (byte) i; } final int kl = key.length(); for (i = 0, j = 0, k = 0; i < 256; i++) { j = j + sbox[i] + key.charAt(k) & 0xff; k = (k + 1) % kl; x = sbox[i]; sbox[i] = sbox[j]; sbox[j] = x; } // Set things up to start coding/decoding i = 0; j = 0; } // Assuming everything was set up earlier, encode plaintext. This can // be done in stream fashion; sequential calls to this routine will be // the same as a single call with a longer parameter. In other words, // as Benny Hill had it in a comedy sequence, // codeDecode("THE")+codeDecode("RAPIST") equals codeDecode("THERAPIST") public String codeDecode(final String plaintext) { byte x; String r = ""; final int pl = plaintext.length(); for (int k = 0; k < pl; k++) { i = i + 1 & 0xff; 181Cryptography Download from www.wowebook.com ptg j = j + sbox[i] & 0xff; x = sbox[i]; sbox[i] = sbox[j]; sbox[j] = x; r+= (char)(plaintext.charAt(k) ^ sbox[sbox[i]+sbox[j] &0xff] &0xff); } return r; } // A simple utility method to simplify setting up the key and // using it for encryption in a single step public String codeDecode(final String key, final String plaintext) { setUp(key); return codeDecode(plaintext); } } In this case, the code can be used both client- and server-side. Just for variety, let’s then reuse it; we have already used JSNI (with MD5) and JCA (again with MD5), so now we’ll opt for sharing the same code. Note that the same codeDecode(...) method is used for both encoding and decoding. To always transmit ASCII legible characters (that will make debugging easier!) let’s add a pair of utility methods to our Security package. The first, byteStringToHexString(...) will convert a String formed by any bytes, to a Hex equivalent; for example, AtoZ\n would become 41746f5a0a, which contains only digits and letters. To revert the effects of this, let’s also have hexStringToByteString(...). The source code can be used both client- and server-side, so that means less coding. public class Security { public static String byteStringToHexString(final String s) { String r = ""; for (int i = 0; i < s.length(); i++) { r += byteToHexChars(s.charAt(i)); } return r; } // Convert a number (0..255) into its two-character equivalent. // For example, 15 returns "0F" and 100 returns "64". public static String byteToHexChars(final int i) { final String s = "0" + Integer.toHexString(i); return s.substring(s.length() - 2); } 182 Chapter 10 Working with Servers Download from www.wowebook.com ptg public static String hexStringToByteString(final String s) { String r = ""; for (int i = 0; i < s.length(); i += 2) { r += (char) Integer.parseInt(s.substring(i, i + 2), 16); } return r; } Finally, on occasion we shall need to produce a nonce, a cryptographic term that stands for “number used once,” but which can actually be any kind of random or time-dependent string. For server-side coding, we can just use code from the Apache Commons Lang component.6 public static String randomCharString() { return RandomStringUtils.randomAlphabetic(32); } On the other hand, we’ll have to whip up our own implementation for client-side coding, because there’s no GWT version of RandomStringUtils. In any case, it’s easy to come up with something like the following. public static String randomCharString() { String r = ""; for (int i = 0; i < 32; i++) { r += (char) ('A' + Random.nextInt(26)); } return r; } We are done; let’s now start applying these methods for our GWT security problems. Stateless Versus Stateful Servers With usual web systems, all the application “state” is kept at the server, whereas the client is used just to display data and to capture events, which are forwarded to the server. Whenever the server receives an event, it does whatever process is necessary and sends a new page to the client, so the user can see an updated display with the effects of his operation. See Figure 10.1. More modern (i.e., Ajax) systems turn this scheme upside down. By taking advantage of local JavaScript processing, most events are managed client-side, and RPC is used to send queries or new data to the server. The server by itself is just a service provider; whenever it receives a request from the client, it updates the system database and sends back any results, which will be displayed by the client side logic. See Figure 10.2. 183Stateless Versus Stateful Servers 6. See http://commons.apache.org/lang/. Download from www.wowebook.com ptg Figure 10.1 In classic web systems, all application state resides at the server, whereas the client is just used for displaying data. 184 Chapter 10 Working with Servers Stateless Client Stateful ServerEvents and Data Screen to Be Shown Stateful Client Stateless ServerProcess Calls Events Service Results Figure 10.2 Modern web systems take advantage of JavaScript and Ajax calls to bring processing closer to the user. It can be argued that moving state away from the server helps provide more scalable systems. With fully stateless servers, should you find a bottleneck in server-side process- ing, you could easily add more servers. With this, clients could connect to any available server, because all the required data would be provided by the client; the server wouldn’t have to “remember” anything. Because of this, we shall be opting for stateless (or, as-little-state-as-possible) server- side coding. This automatically implies that the client-side code will have to identify itself before asking for any processing. The server-side code will have first to validate whether the user is a valid one, and authorize to perform whichever process he might ask, and if everything is OK then proceed with the request and send back the produced results. To enhance security, both sides (client and server) will have to share a “secret”; that is, information known only to both of them. A client-side easy secret is the user password; however, we cannot just send it over the web connection, for that would be quite risky. We shall recur to creating a special “session key” that will be used for identification and also for encryption and hashing; we shall see this next. Remembering the secret for each client-side user can be done either with a database or by using HTML sessions. We’ll use the first solution (so our servers won’t be 100% state free) but that’s the only state data that will be kept at the server. Download from www.wowebook.com ptg Common Operations In this section let’s consider common operations (such as logging in or changing your password) that require extra care to avoid security risks. Logging In We’ll start with a typical “what’s wrong with this picture?” puzzle and consider the fol- lowing code, taken from the Login Presenter we saw back in Chapter 4, “Working with Browsers.” Remember the somewhat meaningless getSomething(...) call? Let’s now figure out what it should do. (We should point out that we still haven’t said what the servlet should return, but that’s not the problem here.7) loginDisplay.setLoginCallback(new SimpleCallback() { @Override public void goBack(final Object result) { final String name = LoginFormPresenter.this.getDisplay() .getName(); final String pass = LoginFormPresenter.this.getDisplay() .getPassword(); loginService.getSomething(name, pass, new AsyncCallback() { ... }); } }); The real problem is that the password is sent “in the clear” to the server. If Eve (our eavesdropping intruder) were to use a sniffer and check all packets sent from your client to the server, she would immediately get a nice user/password pair, which would enable her to fully impersonate a valid user! It’s time for some cryptography, but some methods just won’t do. For example, send- ing a hash of the password instead of the actual password wouldn’t work either; Eve (with the aid of Mallory, perhaps) could do a fake login and send the username plus the key hash, and she’d also be in. Sending an encoded password, but using always a fixed key for encoding, would present the same problem. The solution is simple: We will generate a nonce and send n Username n Nonce n Hash of the user password concatenated to the nonce 185Common Operations 7. For another take on secure logins, see http://code.google.com/p/google-web-toolkit-incubator/ wiki/LoginSecurityFAQ. Download from www.wowebook.com ptg Unless an intruder knows your password, he cannot produce the correct hash, so this is a safer method. (And, in any case, we shall be adding more protection to our commu- nications, by using signatures.) The code for this would be simple; we’ll reuse the MD5 logic we saw back in Chapter 8.8 loginDisplay.setLoginCallback(new SimpleCallback() { @Override public void goBack(final Object result) { LoginFormPresenter.this.getDisplay().enableLoginButton(false); final String name = LoginFormPresenter.this.getDisplay().getName(); final String pass = LoginFormPresenter.this.getDisplay() .getPassword(); final String nonce = Security.randomCharString(); final String hashPassword = Security.md5(nonce + pass); loginService.getSessionKey(name, nonce, hashPassword, new AsyncCallback() { @Override public void onFailure(final Throwable caught) { LoginFormPresenter.this.getEnvironment().showAlert( "Failed login"); LoginFormPresenter.this.getDisplay().enableLoginButton( true); loginSuccessCallback.onFailure(new Throwable()); } @Override public void onSuccess( final SessionKeyServiceReturnDto result) { final String calculatedHash = Security.md5(nonce + result.encryptedSessionKey); if (result.hash.equals(calculatedHash)) { final Security secure = new Security(); final String sessionKey = secure .codeDecode( pass + nonce, Security.hexStringToByteString( result.encryptedSessionKey)); 186 Chapter 10 Working with Servers 8. It has been said that MD5 isn’t a good hashing method any longer, and that you should replace it with SHA-1 or better. It can be argued that for these kinds of values, MD5 could be appropriate, but in any case we are more interested in the general logic than on the specific hash function to apply. I went with MD5 because we had already used it earlier in the book, and because its known problems (such as producing two different strings that hash to the same value) do not necessarily apply in this context. Download from www.wowebook.com ptg loginSuccessCallback.goBack(new UserPassKeyDto(name, pass, sessionKey)); } else { LoginFormPresenter.this.getEnvironment().showAlert( "Wrong data - problem in communication!"); LoginFormPresenter.this.getDisplay() .enableLoginButton(true); loginSuccessCallback.onFailure(new Throwable()); } } }); } }); The preceding code modifies the “Login” button callback in our Login Form. Because we are just trying to log in (this is our first attempt at communicating with the server) there’s no shared secret (session key) yet. We shall call the getSessionKey(...) method (getSessionKey(...) is a better name than getSomething(...), isn’t it?) with the user name, a nonce, and a hash of the password concatenated with the nonce. If the server accepts this login (we shall be seeing the server side code in a moment) it will return an encrypted version of the session key, plus a hash code. We use a DTO for this; remember to have it implement IsSerializable, and add an empty constructor as we saw in Chapter 6. package com.fkereki.mvpproject.client.dtos; public class SessionKeyServiceReturnDto extends GenericServiceReturnDto { public String encryptedSessionKey; public SessionKeyServiceReturnDto() { } } The base GenericServiceReturnDto class will be used for all data exchanges that require security checking. Notice that we have one only attribute (hash); other attributes will have to be added depending on what your service must return. Also note that none of the secret parameters (user password, session key, and even the nonce) are stored within the DTO; sending them over the wire would be a really dumb move! package com.fkereki.mvpproject.client.dtos; import com.google.gwt.user.client.rpc.IsSerializable; public abstract class GenericServiceReturnDto implements IsSerializable { 187Common Operations Download from www.wowebook.com ptg /* * Each extended subclass will add some data. The "hash" field must be * calculated using the (non-included) nonce, the other data, and the * (non-included) sessionkey. */ public String hash; public GenericServiceReturnDto() { } } The client, on receiving the DTO shown here, will have to validate whether the hash coincides with the data, and if so, will decrypt the session key, which was encrypted using the user password plus a nonce. The rationale for using the user password for this encryption is that nobody will be able to decrypt the session key unless he knows the user password. And, the reason for using an extra nonce is that the same key should never be used twice, to avoid some possible attacks. The required server-side coding is as follows. @Override public SessionKeyServiceReturnDto getSessionKey( final String name, final String nonce, final String passHash) throws FailedLoginException { final String password = ...get the password for "name" from the db...; // check the received data by means of the hash final String calculatedHash = Security.md5(nonce + password); // if there's a match, create a sessionKey and send it back if (passHash.equals(calculatedHash)) { final String sessionKey = Security.randomCharString() .toLowerCase(); // store the session key from the session // (alternative: store the key at the DB) final HttpServletRequest request = getThreadLocalRequest(); final HttpSession session = request.getSession(); session.setAttribute(SESSION_KEY_ID, sessionKey); final Security secure = new Security(); final String coded = secure.codeDecode(password + nonce, sessionKey); final String hexCoded = Security.byteStringToHexString(coded); 188 Chapter 10 Working with Servers Download from www.wowebook.com ptg final SessionKeyServiceReturnDto sk = new SessionKeyServiceReturnDto(); sk.encryptedSessionKey = hexCoded; sk.hash = Security.md5(nonce + hexCoded); return sk; } else { throw new FailedLoginException(); } } The server starts by calculating the hash it should have received; if there were some meddling with the data or the hash, there won’t be a match, and the login attempt will be rejected. On the other hand, if there is a match, the server can be fairly confident that the user at the other end is the correct one; nobody else could have calculated the pro- vided hash, which depended on the password. (And, nice point, nobody can determine the password from the hash; that’s a key characteristic of hashes.) After this, the server can just generate a random session key, encrypt it (with the user password concatenated with the received nonce), and send it together with a hash, so the client can recheck the validity of the transmission. Even if Mallory faked the first call to the server, he wouldn’t still have the password (unless he actually “0wn3d” your client machine, and then he could obviously do any- thing he desired!) and he needs the said password for other steps, as we’ll see next. Also, the server could keep a list of client-used nonces and refuse to accept a login with a repeated value, which would deny Mallory’s replay attempt. (In this database, passwords are stored “in the clear,” but we could easily plug that poten- tial security hole. We could hash the password before storing it in the users table, and then the user, instead of hashing the nonce plus his password, would hash the nonce plus the hash of his password, and the checking procedure at the server would be similarly changed.) As a final question, where should the server store the session key it just generated? A logical possibility would be using a server-side session, and write code such as9 final HttpServletRequest request = getThreadLocalRequest(); final HttpSession session = request.getSession(); session.setAttribute("sessionkey", generatedSessionKey); Retrieving the session key at a later time would merely involve final HttpServletRequest request = getThreadLocalRequest(); final HttpSession session = request.getSession(); return (String) session.getAttribute("sessionkey"); Notice that you can use a server-side session to store as many key/value pairs as you want, but we use it just for the session key. The simplest way out, as we mentioned earlier, 189Common Operations 9. Note that this is pure Java code and actually has nothing to do with GWT. Download from www.wowebook.com ptg is using the database itself, and storing the session key along with the user data. (Another, better, possibility would involve using a separate table including a timestamp, the user- name, the nonce sent by the user, and the session key created by the server. This table would also do as a log for login attempts.) Logic for this is straightforward, so let’s move on to more complicated operations. Changing Your Password The preceding login code is particular, insofar that the session key cannot be used— mainly because it hasn’t been determined yet! Let’s now consider other processes, requir- ing the client to send sensitive data to the server, such as a new password.10 Let’s first consider our “Change Password” form. The view (done with UIBinder) is simple. See Figure 10.3.

ChangePasswordView

190 Chapter 10 Working with Servers 10. It should be repeated here, that it’s not likely that you will require such levels of secrecy for all operations; however, if you do, the methods shown here will be adequate. (And if you did require such privacy, you should rather use SSL, encrypt all transmissions in the safest possible way, and forget your problems.) Download from www.wowebook.com ptg 191Common Operations Figure 10.3 A simple password change form requires hashes and encryption to safely send the new password from the client to the server. The Display interface is similar to the Login interface and has a setter for the name field, a couple of getters for the password fields, a method for enabling or disabling the Change Password button, a callback for handling the blur events on both password fields, and a callback for the click event of the button. The corresponding View code is simple, so let’s move to the Presenter. We don’t want the user to attempt changing his password, unless he has entered it twice, and both data entries match. We can use a common blur handler for both password fields. package com.fkereki.mvpproject.client.changePassword; // ...imports... public class ChangePasswordFormPresenter extends Presenter { public static String PLACE = "change"; LoginServiceAsync loginService; SimpleCallback loginSuccessCallback; public ChangePasswordFormPresenter( final String params, final ChangePasswordFormDisplay loginDisplay, final Environment environment) { super(params, loginDisplay, environment); loginService = getEnvironment().getModel().getRemoteLoginService(); final SimpleCallback commonBlurHandler = new SimpleCallback() { @Override public void goBack(final Object result) { final String pass1 = ChangePasswordFormPresenter.this .getDisplay().getPassword1(); Download from www.wowebook.com ptg final String pass2 = ChangePasswordFormPresenter.this .getDisplay().getPassword2(); final boolean canLogin = !pass1.isEmpty() & pass1.equals(pass2); ChangePasswordFormPresenter.this.getDisplay() .enableChangePasswordButton(canLogin); } }; loginDisplay.setPasswordBlurCallback(commonBlurHandler); commonBlurHandler.goBack(null); Initializing the form is quite straightforward. Notice we require some new methods in the Environment object: We’ll store in it the current user (whose name we show in a read-only field), the current password (which the user entered in the login form), and the current session key (which was obtained when the user logged in.) final String currentUser = environment.getCurrentUserName(); final String currentKey = environment.getCurrentSessionKey(); final String currentPass = environment.getCurrentUserPassword(); loginDisplay.setName(currentUser); The only part missing from the code is the click handler for the Change Login but- ton. We get the new password from the form, generate a nonce, and use it plus the user (current) password and the session key to encrypt the new password. Adding a hash (to avoid data tampering), we call the changePassword(...) method. If the password change is successful, we have to change the password in the environment; otherwise, the server (which already has the new password) wouldn’t decrypt future encrypted data. loginDisplay .setChangePasswordCallback(new SimpleCallback() { @Override public void goBack(final Object result) { ChangePasswordFormPresenter.this.getDisplay() .enableChangePasswordButton(false); final String pass1 = ChangePasswordFormPresenter.this .getDisplay().getPassword1(); final Security sc = new Security(); final String nonce = Security.randomCharString(); final String encryptedPass1 = sc.codeDecode(nonce + currentPass + currentKey, pass1); final String visibleEncryptedPass1 = Security .byteStringToHexString(encryptedPass1); 192 Chapter 10 Working with Servers Download from www.wowebook.com ptg final String hashPassword = Security.md5(nonce + visibleEncryptedPass1 + environment.getCurrentSessionKey()); loginService.changePassword(currentUser, visibleEncryptedPass1, nonce, hashPassword, new AsyncCallback() { @Override public void onFailure(final Throwable caught) { ChangePasswordFormPresenter.this.getEnvironment() .showAlert("Failed change"); ChangePasswordFormPresenter.this.getDisplay() .enableChangePasswordButton(true); } public void onSuccess(final Void result) { ChangePasswordFormPresenter.this.getEnvironment() .showAlert("Password was changed"); ChangePasswordFormPresenter.this.getEnvironment() .setCurrentUserPassword(pass1); ChangePasswordFormPresenter.this.getDisplay() .enableChangePasswordButton(true); } }); } }); } } The scheme showed in this case can be used for any kind of data exchange. For example, if we had a service that sent back sensitive data, it would encrypt it (using the user password, the session key, and a nonce), and add a hash. The client would then have to recalculate the hash, and if it matched, then it would decrypt the data and use it. Summary All Internet applications can be vulnerable to malicious third parties, and thus you’ll have to take security in consideration whenever you program with GWT. (Also, read the ref- erence mentioned in footnote 2 on security; there are many ways to take in a user.) Fortunately, the required steps are simple and easy to apply. On the other hand, it’s a bit sad to notice that unless you go with the https:// solution, anything you plan or do might be eventually subverted; for example, the user could be tricked into connecting to a malicious server! 193Summary Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 11 Moving Around Files Sending files to a server, or receiving a file from a server, is a common requirement, and GWT allows you to do so, but you’ll have to use tools we haven’t yet seen. In this chapter we’ll study these matters, see how to provide feedback to the user as the process runs, and even get to use different ways of communicating with the server, complement- ing the methods we already studied. Uploading Files Given the browser restrictions on client-side file handling (meaning, basically you cannot do anything at all with files!) uploading any files to a server requires more “classical” web programming, like submitting forms to send the data; we won’t be able to apply any of GWT or Ajax techniques to work otherwise. We’ll start with a simple file upload form, then consider the server-side servlet that will receive and process it, and finally move on to study how to give feedback to the user while this whole job is being done. An Upload Form Let’s work with an as-easy-as-possible upload form, with absolutely no frills. (See Figure 11.1.) I named the form FileUpload, so the UiBinder code is FileUploadView.ui.xml. We could have several file upload fields, if we wanted to send up many files at the same time, just as with any web page.1 (And to prepare for this, the server-side code will include a loop, so it could receive any number of files; see next.) 1. With the upcoming HTML 5 new features, you could allow for drag-and-drop selection of files, and apply JSNI to do some JavaScript local processing, but you cannot depend on that right now because it’s not widely implemented. To learn more about this, see the latest draft at http:// dev.w3.org/html5/spec/Overview.html for more data on the new element and on drag-and-drop methods. Download from www.wowebook.com ptg Figure 11.1 A no-frills file upload form. We could as easily upload several files at the same time. The interesting point is that the only way to submit files to a server is through a form, and thus we’ll have to enclose the FileUpload widgets within a FormPanel. Another important point is that you must give each FileUpload widget a name; other- wise, you won’t be able to process those files server-side.2

FileUploadView

196 Chapter 11 Moving Around Files 2. Forgetting this is easy and will lead to producing a zero-length list in the file processing servlet, when you do upload.parseRequest(request)... guess how I know? Download from www.wowebook.com ptg You will also have to set three FormPanel parameters (or your upload will fail) and I opted to do this in the View code, though it could be argued that setting them from the Presenter would be better because it would allow applying JUnit testing as in Chapter 13, “Testing Your GWT Application”: n Set the form’s Action to the path for your file processing servlet. n Set the encoding to Multipart. n Set the method to POST; you cannot send files otherwise. This form’s Display interface will provide a getter for the filename (so we can check whether to submit the form), a callback to handle the click the Upload File button, a callback to process (and possibly cancel, if some condition isn’t satisfied) the Submit event that will be fired by the button, another callback to handle the Submit Complete event (which is fired when the submission is done), and a method to actually submit the form. package com.fkereki.mvpproject.client.fileUpload; import com.fkereki.mvpproject.client.Display; import com.fkereki.mvpproject.client.SimpleCallback; import com.google.gwt.user.client.ui.FormPanel; public interface FileUploadDisplay extends Display { String getFileToUploadName(); void setSubmitCallback(SimpleCallback scb); void setSubmitCompleteCallback( SimpleCallback scb); void setUploadClickCallback(SimpleCallback scb); void submitForm(); } The corresponding view is in FileUploadView.java: package com.fkereki.mvpproject.client.fileUpload; // ...imports... public class FileUploadView extends View implements FileUploadDisplay { 197Uploading Files Download from www.wowebook.com ptg @UiTemplate("FileUploadView.ui.xml") interface Binder extends UiBinder { } @UiField FormPanel uploadForm; @UiField FileUpload fileToUpload; @UiField Button uploadButton; SimpleCallback onSubmitCompleteCallback; SimpleCallback onSubmitCallback; SimpleCallback onUploadClickCallback; private static final Binder binder = GWT.create(Binder.class); public FileUploadView() { final FormPanel dlp = binder.createAndBindUi(this); initWidget(dlp); uploadForm.setAction("/mvpproject/fileprocess"); uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART); uploadForm.setMethod(FormPanel.METHOD_POST); } @Override public String getFileToUploadName() { return fileToUpload.getFilename(); } @UiHandler("uploadForm") public void onSubmitComplete(final SubmitCompleteEvent event) { onSubmitCompleteCallback.goBack(event); } @UiHandler("uploadForm") public void onSubmitForm(final SubmitEvent event) { onSubmitCallback.goBack(event); } @Override public void setSubmitCallback(final SimpleCallback scb) { onSubmitCallback = scb; } @Override public void setSubmitCompleteCallback( final SimpleCallback scb) { 198 Chapter 11 Moving Around Files Download from www.wowebook.com ptg onSubmitCompleteCallback = scb; } @Override public void setUploadClickCallback(final SimpleCallback scb) { onUploadClickCallback = scb; } @Override public void submitForm() { uploadForm.submit(); } @UiHandler("uploadButton") void uiOnUploadClick(final ClickEvent event) { onUploadClickCallback.goBack(null); } } Let’s now go to the Presenter code, which actually does the work. package com.fkereki.mvpproject.client.fileUpload; // ...imports... public class FileUploadPresenter extends Presenter { public static String PLACE = "upload"; public FileUploadPresenter( final String params, final FileUploadDisplay fileUploadDisplay, final Environment environment) { super(params, fileUploadDisplay, environment); fileUploadDisplay .setUploadClickCallback(new SimpleCallback() { @Override public void goBack(final Object result) { if (getDisplay().getFileToUploadName().isEmpty()) { getEnvironment().showAlert( "You must pick a file to upload."); } else { getDisplay().submitForm(); } } }); 199Uploading Files Download from www.wowebook.com ptg fileUploadDisplay .setSubmitCallback(new SimpleCallback() { @Override public void goBack(final SubmitEvent result) { // you could check for special conditions // if the event cannot proceed, then do: // result.cancel(); // As an alternative, these checks could go // in the button click method. } }); fileUploadDisplay .setSubmitCompleteCallback(new SimpleCallback() { @Override public void goBack(final SubmitCompleteEvent result) { // do something when the file process is done } }); } } We aren’t including any feedback here while the file is being uploaded; we’ll get to that later, and it will involve adding some code to the Submit event handler. A File Processing Servlet Now, let’s turn to the server-side file processing servlet. (Of course, this code could also be a PHP script, for example, because we are dealing with standard HTML form sub- mission; let’s keep to Java to go along with GWT’s orientation.) Get and add the Apache Commons commons-fileupload and commons-io jars to your application, and then you’ll be able to easily get and process the uploaded files.3 You’ll also have to modify the web.xml file for your project, to include the informa- tion on your servlet. In a rapt of inspiration, I named the servlet fileProcess, and thus I added the following lines to the said XML file: fileProcess com.fkereki.mvpproject.server.FileProcess 200 Chapter 11 Moving Around Files 3. See http://commons.apache.org/ for this. Download from www.wowebook.com ptg fileProcess /mvpproject/fileprocess Our servlet code is as follows. For now, we’ll just care about the doPost(...) method; we’ll later use the doGet(...) method to provide feedback to the user. package com.fkereki.mvpproject.server; // ...imports... public class FileProcess extends HttpServlet { @Override protected void doPost( final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final FileItemFactory factory = new DiskFileItemFactory(); final ServletFileUpload upload = new ServletFileUpload(factory); try { final List itemsList = upload.parseRequest(request); for (final FileItem item : itemsList) { if (!item.isFormField()) { final InputStream input = item.getInputStream(); final FileOutputStream output = new FileOutputStream( "/tmp/dummy"); final byte[] buf = new byte[1024]; int len; while ((len = input.read(buf)) > 0) { output.write(buf, 0, len); } output.close(); input.close(); } } } catch (final FileUploadException e) { throw new ServletException(e.getMessage()); } } } 201Uploading Files Download from www.wowebook.com ptg We are just receiving the file and saving it to /tmp/dummy—probably not quite useful in a true-life system! The code uses the Commons API; there’s nothing GWT-specific in there. This kind of programming is actually quite classic non-Ajax servlet code; for example, you could have also provided parameters to the servlet by using hidden fields or form fields, if desired. You could even have access to the session and apply all the usual web processing methods you knew before using GWT.4 As to the code itself, note there’s a loop that processes all form items, discarding mere fields, to process all the uploaded files. (This would allow you to process several files at once.) You can use the getSize(...) method to get the file total size (and possibly dis- card too large files), and getName(...) to get the original file name, among other methods. Providing Feedback to the User If you are uploading large files, or if their process can take some time, it would be better from the user’s point of view if you were to provide him with some kind of feedback. However, when most (if not all) servlet containers receive a request, they store it inter- nally in their entirety before invoking your code; thus, you won’t be able to provide any useful feedback to the user: It would just jump from 0% to 100% with no in-between moments.5 Given this restriction, let’s at least work on providing some feedback during the actual file process. We could store information on the advance of our code during the process- ing loop (the session would be a simple solution) and send it back to the user with the doGet(...) method. We’d change the main file writing part of our code like this: final byte[] buf = new byte[1024]; final long size= item.getSize(); int len; int processed = 0; while ((len = input.read(buf)) > 0) { output.write(buf, 0, len); processed += len; request.getSession().setAttribute("processed", processed + "/" + size); } We use processed to keep count of how many bytes have been read, and we store the total length of the file in size. Finally, we set up a string such as 1024/22960 (meaning, 1024 bytes read out of 22960) and store it in the processed attribute of the session, so the user can query it. Of course, we could easily ramp the level of the feed- back information and provide more data, but this will do for an example. We must also 202 Chapter 11 Moving Around Files 4. In fact, we will be doing this in order to provide feedback. 5. Check the ProgressListener interface for more on this; you can find usage documentation at http://commons.apache.org/fileupload/using.html. Download from www.wowebook.com ptg write the doGet(...) method, which shall just access the session and return the progress information just stored. @Override protected void doGet( final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { response.getOutputStream().print( (String) request.getSession().getAttribute("processed")); } How should the client get and use this information? Let’s first consider how to call the servlet and do a GET and then move on to other considerations. As we are not working with RPC, we’ll have to use the RequestBuilder class to do direct Ajax calls. Possibly in the Submit event code at the Presenter, you would add something such as final RequestBuilder builder = new RequestBuilder( RequestBuilder.GET, "/mvpproject/fileprocess"); builder.setCallback(new RequestCallback() { @Override public void onError( final Request request, final Throwable exception) { // warn on error... } @Override public void onResponseReceived( final Request request, final Response response) { // use response.getText() to get the service returned value // and then use it to provide feedback to the user } }); Given this, you would do a GET by simply writing try { builder.send(); } catch (final RequestException e) { // warn the user if the call failed } 203Uploading Files Download from www.wowebook.com ptg How and when would you use this code? I’ll leave the details up to you, but the sim- plest way would be creating a Timer (at the Submit event) and scheduling it to run, say, every 2 seconds (2000 milliseconds).6 At the Submit Complete event, you would can- cel(...) the timer. The Timer’s run(...) method would do the GET with the pre- ceding code and use the returned value to update some label or process bar on the form. Downloading Files After all the work we did to upload files, the counterpart, downloading a file, is an anti- climax; the code is quite simple, and the only possible complications could lay server- side, to produce the required result.7 We shall see two ways of getting a file; either by posting the form (as with the file upload example) or by using a more classic link. A File Download Form As earlier, let’s go with a simple form. (See Figure 11.2.) We’ll provide our (simple, make believe) servlet with three parameters; it would be just as easy to include more, or to add hidden fields to the form. 204 Chapter 11 Moving Around Files 6. Even better, schedule it to run once, and when it runs, have the timer schedule itself again in 2 more seconds, so you’ll avoid creating a bunch of pending requests should there be some kind of slowdown. 7. For example, if you want to produce reports in several different formats, you might want to con- sider JasperReports (see http://jasperforge.org/) or Pentaho (at www.pentaho.com/), but note that using these tools won’t have anything to do with GWT; it’s a pure server-side coding effort. Figure 11.2 This form will invoke a servlet, which will produce a text file; real-life applications would get a PDF, a spreadsheet, or the like. The FileDownloadView.ui.xml file is similar to the one we used for uploads; let’s just see part of it, including the third parameter, the button, and the link. Download from www.wowebook.com ptg The Display interface will provide getters for the three parameters, methods for setting the callbacks for the button and link click handlers, a setter for the destination (href ) of the link, and methods for setting the Submit event handler and for actually submitting the form. package com.fkereki.mvpproject.client.fileDownload; // ...imports... public interface FileDownloadDisplay extends Display { String getParameter1(); String getParameter2(); String getParameter3(); void setDownloadClickCallback(SimpleCallback scb); void setDownloadLinkClickCallback(SimpleCallback scb); void setLinkHref(String href); void setSubmitCallback(SimpleCallback scb); void submitForm(); } The View code (FormDownloadView.java) is simple, and we can skip it. Let’s just consider the constructor, which sets the form parameters. Note that this time, we use a GET method instead of a POST, and we invoke a different servlet, fileproduce, which we’ll be seeing next: 205Downloading Files Download from www.wowebook.com ptg public FileDownloadView() { final FormPanel dlp = binder.createAndBindUi(this); initWidget(dlp); downloadForm.setAction("/mvpproject/fileproduce"); downloadForm.setMethod(FormPanel.METHOD_GET); } To finish, let’s consider the Presenter code. Setting callbacks is similar to the upload code; the only interesting part is the link click callback. In it, we have to get the form parameters, encode them appropriately so they can be sent to the server, and dynamically create the destination for the link. package com.fkereki.mvpproject.client.fileDownload; // ...imports... public class FileDownloadPresenter extends Presenter { public static String PLACE = "download"; public FileDownloadPresenter( final String params, final FileDownloadDisplay fileDownloadDisplay, final Environment environment) { super(params, fileDownloadDisplay, environment); fileDownloadDisplay .setDownloadLinkClickCallback(new SimpleCallback() { @Override public void goBack(final Object result) { final String param1 = URL.encode(getDisplay() .getParameter1()); final String param2 = URL.encode(getDisplay() .getParameter2()); final String param3 = URL.encode(getDisplay() .getParameter3()); getDisplay() .setLinkHref( "/mvpproject/fileproduce?parameter1=" + param1 + "¶meter2=" + param2 + "¶meter3=" + param3); } }); 206 Chapter 11 Moving Around Files Download from www.wowebook.com ptg // set the other callbacks } } With this code, either by clicking the button (which will do a classic form submis- sion) or on the link, the user will invoke a remote servlet, which will generate a file. A Sample File Producing Servlet In a real-life application, you would be invoking a servlet that would most likely produce some kind of report or spreadsheet, but for this example let’s go for something rather more trivial and just send back a text file including the received form parameters as its contents. This is straightforward server-side Java programming; the only points you must remember is setting its content type and disposition.8 The rest of the doGet(...) method is simple; we just output the received parameters to the request OutputStream and close it. package com.fkereki.mvpproject.server; // ...imports... public class FileProduce extends HttpServlet { @Override protected void doGet( final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { // media type (or, more modern, content type) // // text/plain application/msexcel final ServletOutputStream output = response.getOutputStream(); response.setContentType("text/plain"); response.setHeader("Content-Disposition", "attachment; filename=somefile.txt"); output.println("Received parameters:"); output.println(request.getParameter("parameter1")); output.println(request.getParameter("parameter2")); output.println(request.getParameter("parameter3")); output.close(); } 207Downloading Files 8. See RFC 2046 at www.ietf.org/rfc/rfc2046.txt for more on this. Download from www.wowebook.com ptg For generality, we can let the servlet answer to POST methods in the same way than to GETs. @Override protected void doPost( final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } Now, running the form and clicking the button or link will call the servlet, produce a somefile.txt text file, and give you the option to open or download it. See Figure 11.3. 208 Chapter 11 Moving Around Files Figure 11.3 Clicking on either the button or the link does a call to the servlet that produces a simple text file as output. It’s most important to note that in this example, we have been mostly using time- tried web programming techniques and not much GWT-original code. (If, for example, you wanted to display the contents in a separate window, you would just add target= "_blank" to the link’s destination; pure classic HTML!) Also, you need not invoke a servlet; it would have been equally simple (just a matter of changing destinations) to interact with PHP or Python scripts; the client-side code doesn’t care, and the server- side programming need not know that it is talking to a GWT client. Download from www.wowebook.com ptg Summary In this chapter we studied both how to upload and download files from a server. These processes required using forms and Ajax, so we got to complement the server-communi- cation techniques seen in previous chapters. We also interacted with common servlets, proving GWT can coexist in a more classic Java-oriented server-side architecture. 209Summary Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 12 Internationalization and Localization Developing applications that can be used in different countries, with different lan- guages, requires applying specific techniques, but GWT simplifies dealing with interna- tionalization (i18n) and localization (l10n) matters. In this chapter we’ll examine the rel- evant tools, and also how to apply them to UiBinder designed forms. If you are developing an application that will potentially be used worldwide, instead of being restricted to an Intranet, you will have to take into account multiculture and multilanguage aspects, so your software is still usable. Since having several separate versions for each required language (or, worse, for each locale; remember American English isn’t the same as British English, for example) can soon become quite unwieldy, you’ll want to use methods that allow developing and maintaining just one code base. GWT provides i18n support with its Constants and ConstantsWithLookup inter- faces allowing you to work with string literals in different languages, and with the Messages interface, which adds singular/plural considerations, as well as to work with UiBinder. Finally, in terms of l10n, GWT lets you deal with different currency or date formats, allowing you to more completely adapt your application to specific groups of users, and that will be the last theme in this chapter.1 Internationalization (i18n) Let’s start by considering how to provide appropriate texts for users in different coun- tries. We’ll first give a quick overview to Java’s standard resource bundles, and then move 1. By the way, if you don’t know where i18n and l10n come from, i18n refers to the fact that there are 18 letters between the initial i and the final n in “internationalization,” and likewise for l10n and “localization.” Download from www.wowebook.com ptg to the ConstantsWithLookup and Messages GWT interfaces, which will let us use those bundles in a quite efficient way.2 All i18n methods require the same libraries, so no matter which one you decide to use, you’ll have to add the line to your gwt.xml configuration file. Resource Bundles In standard Java programming, internationalization is usually done by means of resource bundles: .properties files with locale-specific data. Although this data might be any- thing (numbers, dates, whatever) most usually we’ll just deal with strings. Each string is identified by a “key,” which must remain constant across different resource bundles. Basically, in your code you will (mostly indirectly, sometimes directly) refer to this key so your program will be locale-independent, inasmuch as what string will be shown shall depend on which locale resource bundle you use. GWT supports generic resource bundles (strings that will be shown if no other more specific locale is chosen), language resource bundles (for example, English or Spanish versions of your strings), and even country-specific resource bundles (such as British English, or Mexican Spanish). You should have a generic bundle file, plus one or more language bundles, plus possibly some country specific bundles. All keys should appear in the generic bundle file. If a certain key appears in several bundles, country strings have priority over language strings, and the latter have priority over the generic ones. For example, suppose we are given these bundles (whose names shall be explained presently). Transport.properties Transport_en_GB.properties Transport_es.properties flight=airplane flight=aeroplane flight=avión vehicle=car vehicle=automóvil underground=subway underground=tube underground=subterráneo sea=ship sea=barco In this case, a British user who wanted to use the underground would get a message about the tube; Spanish users would get references to the subterráneo; ever ybody else (including other non-British English speakers) would get the subway standard reference. (GWT considers English as the standard language.) Note that you don’t have to repeat keys in all files; all British users would get car for vehicle, for example, because they don’t have a specific string value for that key. You should always provide a basic, standard reference (though you don’t need to use a resource bundle for this, because you can do with annotations; see the following) plus 212 Chapter 12 Internationalization and Localization 2. If you also have to support RTL (right-to-left) languages such as Arabic or Hebrew, using the tech- niques in this chapter won’t be enough, for you’ll also have to change the visual theme; see http://code.google.com/webtoolkit/doc/latest/DevGuideUiCss.html#themes for more on this. Download from www.wowebook.com ptg resource bundles for each language you plan to support, eventually even going as far as to provide files for specific countries. Resource bundles must be named with the interface name (see next) optionally fol- lowed by an underscore and a lowercase two-character language specification, and possi- bly another underscore and an uppercase two-character country code.3 A final reminder: Resource bundles must be written in UTF-8 if you require foreign letters or accents. Be sure to include the line in your .html file, and also to configure your resource bundle editor to use this charac- ter set.4 Using Constants The Constants and ConstantsWithLookup interfaces bind, at compile time, the pro- vided resource bundles with your provided code, to produce locale-specific versions of the code. (We’ll be seeing the specific code generator mechanism in a short while.) Whenever the user browses to your application, the loader code determines your browser type (as we mentioned in Chapter 4) and your locale, and then loads the compiled version of your system that matches those two parameters. (As we saw earlier, this is as efficient as it gets, for you download only code that suits your situation perfectly, instead of a large suit-everybody version, with support for all browsers and all languages, even if you don’t require them.) We will first use the simpler Constants interface, and then see why and when we would prefer the other one.5 For a simple example, say you wanted to greet a user. You could have a generic mes- sage and a more specific one, along the (not very original!) lines of genericHello=Hello there! specificQuery=How are you today? For British speakers you might change the first line to Hullo there! (do note the spelling of the first word) whereas for Spanish users you would provide something with plenty of special characters, along the lines of genericHello=¡Hola! specificQuery=¿Cómo estás hoy? 213Internationalization (i18n) 3. The language codes are taken from the ISO 639-1 standard (see www.infoterm.info/standardization/ iso_639_1_2002.php) whereas country codes (which we already used in Chapter 6) are from ISO 3166-1. 4. For example, in Eclipse you should go to Edit, Set Encoding and pick UTF-8; otherwise, your file won’t match expectations and results will look weird, to say the least. 5. Note that constants need not be Strings (for you can also provide Boolean, float, integer, and so on constants) but as far as internationalization goes, this is by far the most common case. Download from www.wowebook.com ptg To use these files, let’s extend the ConstantsWithLookup interface. (We could as eas- ily have extended Constants, but as I want to later show dynamic string lookup, I went with ConstantsWithLookup.) If we opted to call it Greet, the Greet.java file could be package com.kereki.testi18n.client; import com.google.gwt.i18n.client.ConstantsWithLookup; public interface Greet extends ConstantsWithLookup { String genericHello(); String specificQuery(); } Because your interface was named Greet, your resource bundles should be named Greet.properties, Greet_en_GB.properties and Greet_es.properties with the contents previously listed; the first would be the generic catch-all, whereas the second would be applied to British users, and the third one for Spanish ones. These bundles must be at the same directory where the source file for your interface resides. Finally, you must let the compiler know about which languages will be supported, by editing the gwt.xml file and adding We can test this internationalization by writing just a pair of lines. final Greet greet = GWT.create(Greet.class); Window.alert(greet.genericHello() + " " + greet.specificQuery()); The first line uses deferred binding to create the appropriate class (depending on the locale of the browser) and the second line displays the required texts.6 If you run this code without any further ado, you’ll get a Hello there! How are you? message. If you want to test the other locales, you can either add a line such as to your .html file (which will set the default language for your application to Spanish) or add the parameter locale=es to the URL in your browser; any of these solutions would produce a ¡Hola! ¿Cómo estás? alternative message. The first solution is best for large scale testing, whereas the URL one (which takes precedence over the other solution) is preferred for quick tests. 214 Chapter 12 Internationalization and Localization 6. For extra efficiency, you should create the greet object just once, and then always refer to it; the Singleton design pattern comes to mind, and is a perfect fit for this situation. Download from www.wowebook.com ptg Some Annotations Tricks You need not have a generic resource bundle, for you can use annotations to provide default values—and that’s better from the point of view of documentation and usage. For example, we might want to have a string describing the kind of users to whom the spe- cific locale version applies. By adding @DefaultStringValue("English speakers") String kindOfUsers(); to the Greet.java file, you would be defining a new text, which by default would be English speakers, without the need for a Greet.properties entry. (You would, however, have to add kindOfUsers=British speakers to the Greet_en_ GB.properties file and kindOfSpeakers=Hablantes de Español to Greet_ es.properties.) Having the default value in the same interface file helps programmers understand the meaning of the strings, without resorting to checking other bundles for tips or hints. For extra clarity, your keys need not match the methods names. By using the @Key(...) annotation, you can specify which is the actual key used in the properties file. You could write @Key("day.morning") @DefaultStringValue("morning") String morning(); @Key("day.afternoon") @DefaultStringValue("afternoon") String afternoon(); This would require adding, in the Spanish resource bundle, lines such as day.morning=Mañana day.afternoon=Tarde (Note that we used @DefaultStringValue(...)to avoid having a generic .properties file, as shown earlier.) The keys could be structured so as to allow a more logical ordering, whereas the interface methods can keep using simple names. Translating Error Codes The preceding code works just fine for texts that are created client-side, but what would you do with, say, an error message that was originated server-side? An i18n aware appli- cation that will display error messages only in English isn’t a very good international application! There are two solutions to this problem; you could also use standard interna- tionalization techniques server-side, or you could send error codes instead of error texts, and have the codes translated into texts client-side. The first technique has nothing to do with GWT, so we’ll skip it, but let’s consider now the ConstantsWithLookup interface to solve the problem client-side. 215Internationalization (i18n) Download from www.wowebook.com ptg This interface adds methods that let you seek the value corresponding to any given key. With the Constants interface, the mapping between keys and methods is static; ConstantsWithLookup adds a dynamic way of getting the required values. For a simple example, if we change our Greet class to extend ConstantsWithLookup (and change nothing else) then the two following lines would produce the same result: Window.alert(greet.kindOfUsers()); Window.alert(greet.getString("kindOfUsers")); The standard pattern of usage would be n Define constants to represent status or error messages. n Modify your remote servlets, so they will return those constants instead of strings. n Use (at the client side) a ConstantsWithLookup class to translate the constants into localized strings. You might be tempted to always use this kind of interface, but to understand when to use each of the two constants interfaces, let’s examine the produced JavaScript code. The compiler makes short work of straight calls; for example, by compiling the application with Pretty style (see Chapter 15, “Deploying Your Application”) we can see that the en_GB version of the code produces our greeting message to British users by simply doing: $wnd.alert('Hullo there! How are you today?'); On the other hand, to fulfill dynamic requests, code such as the following will be generated, including all possible strings. (This is logical because there’s no way to tell which key you might ask for.) Even though the code is efficient and uses a cache to avoid re-doing searches, it still must include every possible key in your GWT code. function $getString(this$static, arg0){ var target; target = dynamicCast($get_1(this$static.cache, arg0), 1); if (target != null) { return target; } if ($equals_1(arg0, 'specificQuery')) { $put(this$static.cache, 'specificQuery', 'How are you today?'); return 'How are you today?'; } if ($equals_1(arg0, 'genericHello')) { $put(this$static.cache, 'genericHello', 'Hullo there!'); return 'Hullo there!'; } if ($equals_1(arg0, 'kindOfUsers')) { $put(this$static.cache, 'kindOfUsers', 'British English Speakers'); return 'British English Speakers'; } 216 Chapter 12 Internationalization and Localization Download from www.wowebook.com ptg throw $MissingResourceException(new MissingResourceException, "Cannot find constant '" + arg0 + "'; expecting a method name"); } So, to get the smallest possible output code, you might consider using a Constants object for the strings you generate client-side, plus a ConstantsWithLookup object exclusively to deal with server-side codes. Messages So far, we have been considering static (i.e., unvarying) messages, most appropriate for captions, warnings, and the like, but applications also require dynamic, varying messages that are built up from fixed and changing elements, such as an “Hasta la vista…” message, to be completed appropriately, as in “Hasta la vista, Baby.” (Of course, this need for vari- able messages also should take into account locales, so the message could be “Auf wieder- sehen…” or “Au revoir…” for Germans or Frenchmen.) GWT supports these localized, variable texts by means of the Messages interface. This interface is very much like the Constants and ConstantsWithLookup inter- faces we saw earlier (you also invoke it in the same way, by using GWT.create(...)) and works by defining keys and strings, with the most important differences that the string values you provide can include special placeholders, and that the methods you write can accept arguments that will be substituted for the placeholders. Let’s create a simple MyMessages interface, which we can use to provide good-bye messages. The format for placeholders is the standard Java MessageFormat style: Parameters to be substituted start at 0, so the single parameter in our sayGoodbye(...) method will replace all {0} occurrences within the string.7 Let’s also produce message showing the year of birth and current age, just to show how to replace more parameters, and how to work with other data types. package com.kereki.testi18n.client; // ...imports... @DefaultLocale("en") public interface MyMessages extends Messages { @DefaultMessage("I was born in {0} so now I''m {1} years old.") String sayAge(int year, int age); @DefaultMessage("Good- bye, {0}") String sayGoodbye(String whom); } 217Internationalization (i18n) 7. See http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html for more on this. Download from www.wowebook.com ptg We are following here the idea of including default values within the interface code to simplify future editing. (Pay particular attention to the doubled-up single quote in the sayAge(...) string.) For British users, we would have a MyMessages_en_GB.proper- ties file with, possibly, a line reading sayGoodbye=Goodbye, {0}! (note the lack of a hyphen in “Goodbye,” and no alternative string for sayAge(...)) whereas Spanish users would require a MyMessages_es.properties resource file with sayGoodbye=¡Hasta la vista, {0}! sayAge=Tengo {1} años porque nací en {0} (The only reason for showing the age first and the birth year second is to prove it can be done; you need not use the parameters in ascending order.) Now, although sayAge(2000,10) produces a perfectly fine I was born in 2000 so now I'm 10 years old, a call such as sayAge(2009,1) produces a queer result: ...now I'm 1 years old. However, the Messages interface provides a way around that. Let’s see first how the code looks and then provide the explanations. We’ll wr ite a new sayAge2(...) method fixing the plural problem. You must pro- vide a generic plural case (...I'm {1} years old) and then add the special cases that, depending on the used language, may apply when the number is zero or one.8 @DefaultMessage("I was born in {0} so now I''m {1} years old.") @PluralText({"one","I was born in {0} so now I''m just one year old." }) String sayAge2(int year, @PluralCount int age); Note the @PluralCount annotation, which indicates which parameter is related to plurals; in this case, we want to treat the parameter indicated as {1} as the special one. In English, zero is treated as plural (...I'm 0 years old) so you just need a special case for one (...I'm just one year old—we need not actually show the 1!). The corre- sponding Spanish resource bundle would include sayAge2=Tengo {1} años porque nací en {0} sayAge2[one]=Tengo un año porque nací en {0} You would provide similar rules for Italian, German, or Portuguese as spoken in Portugal. For Portuguese as spoken in Brazil (or for French) rules vary, because zero is considered singular. sayAge2=Eu sou {1} anos de idade porque eu nasci em {0} sayAge2[one]=Eu sou um ano de idade porque eu nasci em {0} sayAge2[none]= Eu sou zero ano de idade porque eu nasci em {0} You must apply the rules pertaining to the specific locale; you cannot, for example, provide a special “zero” case for English, because the standard rules say it’s to be treated 218 Chapter 12 Internationalization and Localization 8. For some languages, such as Arabic, there are special plural forms for number two and even for small numbers, but for most (if not all) western languages, the only special cases you’ll have to con- sider are zero and one. Download from www.wowebook.com ptg as a plural. If you want to check which rules apply for each language, there’s no official source other than the actual GWT Java code; check it out9 and then examine the DefaultRule*java files at the trunk/user/src/com/google/gwt/i18n/client/ impl/plurals/ directory. UiBinder Internationalization Let’s end this chapter by considering how to translate complete pages. Of course, you could build screens up field by field and text by text, using the methods in previous sec- tions to provide the required translations. You would probably be better off without using UiBinder, but you could make it work. This solution, however, wouldn’t solve all the possible i18n considerations: For exam- ple, whereas in western countries the family name follows the first name, in eastern countries (such as Japan, China, and Korea) it’s the other way round: the family name comes first, and the first name follows. If you wanted to reorder two TextBoxes repre- senting the family and first names to comply with custom, you would have to add some extra coding. GWT provides a Messages-based way to apply internationalization to UiBinder forms that, even though it isn’t as polished as the rest of the tools, can be used with little effort.10 Let’s first examine a simple screen (see Figure 12.1) with some text intermin- gled with fields. 219Internationalization (i18n) 9. See http://code.google.com/webtoolkit/makinggwtbetter.html#checkingout for instructions on this. 10. The fact that this solution is still lacking some polish can be verified by reading the discussion at the bottom of http://code.google.com/p/google-web-toolkit/wiki/UiBinderI18n and the follow-up thread at http://code.google.com/p/google-web-toolkit/issues/detail?id=4355. Figure 12.1 A nonsense form for UiBinder-based internationalization Let’s summarize the whole process in advance. To internationalize a UiBinder form, you will have to add several elements to the form XML code. These elements will iden- tify the parts of the form that need translation and provide placeholders for fields and other nonvarying components. When you compile your project, a resource bundle will be generated for your form, with a line for every item that must be translated, in Download from www.wowebook.com ptg Messages style. To finish the job, you will have to provide translations for each language you want to support; you will probably require a team of (human) translators for this task.11 When the form is shown, UiBinder will use a hidden Messages interface to translate all onscreen terms. Now, let’s get to details. The standard ui.xml file for the form would have along the lines of
How are you today?

Input a value:
Please, think before clicking the button. Please click me!
To use UiBinder’s i18n facilities, you have to add several elements to the preceding file, and the complete file (we’ll get to explanations in a moment) should become as fol- lows. First, you need to add some boilerplate at the element, so the nec- essary methods will be invoked and i18n binding will happen. The only attribute you might want to change (if English isn’t your default language) is defaultLocale. HTML elements need no special notation. General text, however, should be enclosed in a element so it will be recognized as something to be translated. The key attribute (equivalent to the @Key(...) annotation) will be used in the resource bundle to identify the enclosed string. You need not specify keys, but in that case GWT will generate them with a MD5 based method; personally, I prefer using my own, more understandable, keys, but to each his own! 220 Chapter 12 Internationalization and Localization 11. Of course, Google Translate could (some day) be integrated into this task.... Download from www.wowebook.com ptg The description attribute appears as a comment in the file to help translators by giving some context. HTML elements within a text are recognized, as in think.
How are you today?

Input a value:
Please, think before clicking the button. You will usually also require translating other terms, such as tooltips or image descrip - tions. Each such attribute shall be enclosed in a element, which must include the name of the attribute, an optional description as an aid to the translator, and an optional key. In case a word might have two or more meanings (the common example is orange, which might be a color or a fruit) you can also add a meaning attribute (such as meaning="the fruit") so the translator can discern which transla- tion to use. Please click me!
When you compile your code, you must include the -soyc parameter.12 (An alternative is including the -extra parameter, and specify a directory for the output translation files.) After the code is compiled, a file will be created within the extras directory, with a name 221Internationalization (i18n) 12. We will be touching on the possible compile parameters in more detail in Chapter 15, when we deal with code splitting. For now, you’ll just have to accept the need for them! Download from www.wowebook.com ptg such as com.kereki.testi18n.client.ExampleMyUiBinderImplGenMessages .properties—in my case, I was working with the com.kereki.testi18n package, and the Example.ui.xml file was in the client subpackage. The contents of this file will look like the following, though I reordered some lines for clarity: # Generated from com.kereki.testi18n.client.ExampleMyUiBinderImplGenMessages # for locale default # Description: Greeting example.salute=How are you today? # 0=arg0 (Example: ), 1=arg1 (Example: ) 3251F2DD00D79AD3E05D89C06E60F1AA=Input a value\: {0}{1} # Description: Urge to reconsider reconsideration=Please, think before clicking the button. # Description: Button text button.text=Please click me\! # Description: tooltip text for button button.title=Produce some kind of result Let’s walk through this. For the first salute, a straight line (example.salute=How are you today?) in the style of what we have already seen, was produced. The line asking to input a value shows what happens if you don’t enter a key; a MD5 based value (3251F2DD00D79AD3E05D89C06E60F1AA) was used instead. Also, notice that placeholders were created for the TextBox; don’t worry about the fact that there are two placeholders for a single field—it’s just the way internationalization sometime works with UiBinder. The think line shows that HTML elements were passed through, without change; a translator would be able to keep them. And, finally, note that two lines were produced for the text and tooltip of the button. What do you do now? The generated file must be copied to the same directory as the ui.xml file, and renamed—but the renaming rules are also somewhat awkward; my Example.ui.xml file required a Spanish resource bundle named ExampleMyUiBinderImplGenMessages_es.properties as follows: # Generated from com.kereki.testi18n.client.ExampleMyUiBinderImplGenMessages # for locale default # 0=arg0 (Example: ), 1=arg1 (Example: ) 3251F2DD00D79AD3E05D89C06E60F1AA=Entre un valor\: {0}{1} # Description: Button text button.text=¡Por favor, oprímame\! 222 Chapter 12 Internationalization and Localization Download from www.wowebook.com ptg # Description: tooltip text for button button.title=Genera algún tipo de resultado # Description: Greeting example.salute=¿Cómo está hoy? # Description: Urge to reconsider reconsideration=Por favor, piense antes de oprimir el botón. Running the same application with locale=es added (see Figure 12.2) produced a wholly translated output… except for the H1 title, which I forgot to include in the ui.xml file! I decided to let this error be, as a reminder of the possible problems you will likely find. 223Localization (l10n) Figure 12.2 The same form, but translated automatically into Spanish. As I said, UiBinder-based internationalization does work, but you’ll have to agree that the work here isn’t as straightforward as earlier. (The need for actually compiling the application with an extra parameter to boot, and the strange names for the resource bun- dles, come to mind.) Documentation isn’t complete (and sometimes even wrong; I had to work out some of the previous details by perusing forum threads or by studying the GWT source code itself) and it’s possible that there will be changes in the near future, so be careful when using this facility.13 Localization (l10n) Localization is a concept akin to internationalization (but far easier to implement) that has to do with dates, time, and numbers representation, rather than with strings transla- tion. After all the work required for full internationalization, the comparable localization tasks will seem like an anticlimax, for you can use it out-of-the-box without any specific adaptation. 13. The current version of the documentation is at http://code.google.com/webtoolkit/doc/latest/ DevGuideUiBinderI18n.html; check it for extra use cases, or for changes in functionality. Download from www.wowebook.com ptg Although you could roll out your own date and number formats (by using a Constants interface, for example) GWT provides specialized resource bundles for most locales, which take care of all needed conversions to and from dates and numbers. Roughly speaking, you could compare GWT’s date and number formatting functionality to Java’s own java.text.SimpleDateFormat and java.text.DecimalFormat packages.14 For example, if you want to get the current date in “short” format, you can simply write String currentDate= DateTimeFormat.getShortDateFormat().format(new Date()) and the correct locale format would be applied. For example, in the US (locale=en) you would get something such as 4/2/10, but with locale=en_GB then 02/04/2010 would be produced; notice that in European fashion, the day comes before the month.15 The following table shows all possible predefined Date and Time formats for locale=en and locale=es. There are also DateTime formats, which are just the concatenation of the corresponding Date and Time formats, so we are skipping those for brevity. toString Fri Apr 02 12:11:44 UYT 2010 Fri Apr 02 12:11:44 UYT 2010 Full Date Friday, April 2, 2010 viernes 2 de abril de 2010 Full Time 12:11:44 PM Etc/GMT+3 12:11:44 p.m. Etc/GMT+3 Long Date April 2, 2010 2 de abril de 2010 Long Time 12:11:44 PM UTC-3 12:11:44 UTC-3 Medium Date Apr 2, 2010 02/04/2010 Medium Time 12:11:44 PM 12:11:44 Short Date 4/2/10 02/04/10 Short Time 12:11 PM 12:11 By the way, the needed data was produced with this code. final Date today = new Date(); GWT.log(today.toString()); GWT.log(DateTimeFormat.getFullDateFormat().format(today)); GWT.log(DateTimeFormat.getFullTimeFormat().format(today)); GWT.log(DateTimeFormat.getLongDateFormat().format(today)); GWT.log(DateTimeFormat.getLongTimeFormat().format(today)); GWT.log(DateTimeFormat.getMediumDateFormat().format(today)); 224 Chapter 12 Internationalization and Localization 14. If you want to learn about all formatting possibilities, check out http://google-web-toolkit .googlecode.com/svn/javadoc/2.0/com/google/gwt/i18n/client/DateTimeFormat.html and http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/i18n/client/ NumberFormat.html. 15. If you want to see the specific rules applied for each locale, download the GWT source code, and see the files at trunk/user/src/com/google/gwt/i18n/client/constants directory. Download from www.wowebook.com ptg GWT.log(DateTimeFormat.getMediumTimeFormat().format(today)); GWT.log(DateTimeFormat.getShortDateFormat().format(today)); GWT.log(DateTimeFormat.getShortTimeFormat().format(today)); If you need your own format (say you want to produce dates in the ISO 8601 standard format, such as 2010-04-02) you can use the getFormat(...) method and provide your own pattern string. The following code would produce the desired ISO format. DateTimeFormat formatIso = DateTimeFormat.getFormat("yyyy-MM-dd"); String dateIso = formatIso.format(new Date()); There are many pattern codes, as in the following table. Note that they are practically the same as in Java16 n G provides the era, AD or BC. n y provides the year; yyyy would be 2010, but yy would be just 10. n M provides the month: MMMM would be the full name (April), MMM a shorter version (Apr), MM the left-padded with zeroes number (04), and M the number (4). n d provides the day in the month. n H provides the hour in 24-hours format (0–23), whereas k provides it in 1–24 format. n h provides the hour in AM/PM format (1–12), whereas K provides it in 0–11 format. n m provides the minute in the hour (0–59). n s provides the second in the minute (0–59). n S provides the milliseconds. n E provides the day of the week, in text; EEEE would be the full name (Friday), and EEE a shorter version (Fri). n a provides the AM/PM marker. n a single quote is used to escape text, as in 'Date=', and if you want an actual quote in the text, you’ll have to write it twice as in 'O''clock'. For all numbers, if you specify the code twice, you’ll get a left-padded with zeroes number: HH would produce 09, whereas H would just produce 9. Finally, you can also use formats to parse strings into Date objects. For example, if we had the ISO format string 1809-02-12 (the birth date of Abraham Lincoln and Charles Darwin, which we saw some chapters ago, in Figure 8.1) and wanted to produce a Date object, we should write DateTimeFormat formatIso = DateTimeFormat.getFormat("yyyy-MM-dd"); Date lincolnDarwin = formatIso.parse("1859-02-12"); 225Localization (l10n) 16. See http://java.sun.com/docs/books/tutorial/i18n/format/simpleDateFormat.html for the pos- sible codes. Java adds the D, F, W, and w codes (standing for the day in the year, the day in the week, the week in the year, and the week in the month) but lacks the Z and v codes shown in the table. Download from www.wowebook.com ptg You can also use the parseStrict(...) method, which is more careful; whereas a date such as February 30th would be accepted by parse(...) and turned into March 2nd, parseStrict(...) would throw IllegalArgumentException instead. Similarly, NumberFormat provides currency, percent, integer, and float formats, according to locale rules; for example, a comma is used to separate thousands in the US (thus, 2,010) but in Spanish a dot is substituted instead (so, 2.010). The basic predefined formats, for locale=en and locale=es, are as follows. Currency $22,919.60 Arg$22.919,60 Decimal 22,919.6 22.919,6 Percent 2,291,960% 2.291.960% Scientific 2E4 2E4 The code that produced this output was merely double number = 22919.60; GWT.log(NumberFormat.getDecimalFormat().format(number)); GWT.log(NumberFormat.getPercentFormat().format(number)); GWT.log(NumberFormat.getScientificFormat().format(number)); GWT.log(NumberFormat.getCurrencyFormat().format(number)); You can immediately see there’s a problem with currencies; for example, Arg$ isn’t the currency symbol either for Spanish, nor for Argentine.17 Yo u c o u l d w r i t e getScientificFormat("UYU") to get the Uruguayan currency format,18 but it returns Ur$22.919,60; once again, a wrong symbol. You can also specify your own formats, by using the following codes to create a for - mat string. n 0 stands for any digit. # also stands for any digit, but if it represents a zero at the left of the number, a space is shown instead.19 n The dash (-) stands for the minus sign. n The period stands for whatever character is used in your country for the fraction separator, and the comma stands for the thousands separator. For example, in Uruguay the period stands for a comma, while the comma stands for a period… rather confusing! 226 Chapter 12 Internationalization and Localization 17. See www.xe.com/symbols.php for a list of currency symbols. 18. The used codes seem to be based on those of ISO 4217, which includes USD for the US Dollar, EUR for the Euro, and GBP for the British Pound, for example. However, there are many countries missing from the list used by GWT. 19. If you were to format James Bond’s secret agent number with format “000”, you’d correctly get “007”, but if you used “###” as a format, you’d get “ 7” instead, with two leading spaces. Download from www.wowebook.com ptg n E (for “Exponent”) is used to separate the mantissa from the exponent, in scientific notation. n % is used to show that the number is to be formatted as a percentage, and thus multiplied by 100. n A single quote is used to escape text, as with Dates. For example, you could format a number to be displayed in 15 columns, with an optional trailing sign, with NumberFormat.getDecimalFormat("###,###,##0.00-"). Finally, you can also use the parse(...) method to transform a string into a number, like in double number = NumberFormat.getDecimalFormat().parse("1234.56"); GWT will throw NumberFormatException if the number cannot be parsed. Summary Designing an application for international use requires more work and preparation than doing a simple single-locale one. However, GWT provides reasonably simple i18n and more straightforward (almost in the “no assembly required” category) l10n tools that let you get the job done with no great complexity. 227Summary Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 13 Testing Your GWT Application Testing, and in particular, fully automated testing, is an important part of the GWT development cycle. In this chapter we’ll study ways of doing so in optimal ways, starting with unit tests and ending with integration and acceptance testing. Why Testing? The idea of testing software is fairly obvious,1 but the GWT philosophy is based on test- ing everything automatically, without anybody’s intervention, as an aid to bug prevention and detection at the earliest time.2 The Google Testing Blog (at http://googletesting .blogspot.com/) goes as far as including the motto “Debugging sucks; Testing Rocks” at the top of the page. Another saying is “If it’s not tested, it’s broken”; untested code can (and must be assumed to) contain any number of bugs. Automatic testing lies at the heart of techniques such as test-driven development (TDD) and methodologies like Scrum3 and Extreme Programming (XP).4 The main idea is to develop code, with a method based on repetitions of a short development cycle loop: n First you write an (obviously failing) automated test case for a new functionality. This isn’t a waste of time; if a test for a yet-unwritten function should pass, then something is obviously very wrong with your test… n Then you write code that implements the said functionality and passes the test. It goes without saying that all previous tests should also pass; your code shouldn’t break other code! 1. Though now and then everybody is tempted to skip testing, because “it’s just so small a change, nothing could go wrong!” 2. If you examine GWT’s own source code, you’ll find lots and lots of the kind of tests we’ll be devel- oping in this chapter. 3. See www.scrumalliance.org/ for more on Scrum. 4. See www.extremeprogramming.org/ for a good introduction to XP. Download from www.wowebook.com ptg n And finally, you refactor the code so that it meets acceptable standards. Getting the code to work, and then enhancing it (while using the recently written test to ensure as far as possible that the refactoring doesn’t break anything) is easier than trying to get the code to work and be elegant at the same time.5 Advantages of Automatically Tested Code Automated tests encourage better, simpler, code designs; help against common problems such as regressions (reintroducing an earlier bug while modifying some seemingly unre- lated code); and inspire more confidence in the quality of the produced software, which can be shown to pass lots of tests, proving that at least it provides some desired level of functionality. Developers with good TDD practice are more likely to focus on what’s actually required (the YAGNI principle, standing for You Ain’t Gonna Need It) and simplicity (think KISS, or Keep It Simple, Stupid) because they only develop what’s actually needed to pass the tests, not worrying about unrequired functionality or unjustified enhance- ments. Also, they are more likely to detect bad designs, side effects, or obscure code, because all these conspire against automated testing. Automated tests also serve, at a certain level, as documentation. Everybody knows that you can never have too many examples, and each test is a living example showing how to use a class, what kind of parameters to provide, and which results you should get. We’ll consider three types of testing for GWT programs: n Unit Tests, which apply to specific classes or methods, testing them one at a time, independently of other classes and methods. These tests must be as fast as possible, so you won’t balk at running them and won’t require a browser. n Integration Tests, which apply, for example, to specific forms or services and imply using a browser for the tests. These tests are slower, but test functionality closer to the user’s requirements. n Acceptance Tests, which are specifically focused on use cases and are oriented to verifying the functionality of the system as a whole. These tests are usually the slowest and largest, and require the most setup and preparation, because they try out the workings of the complete system. We’ll use common tools (such as JUnit or Selenium) for some of these tests and GWT specific extensions (GWTTestCase) for others. And if a Bug Appears? A basic theorem of Computing Theory states that testing can never prove the absence of errors but only their presence. Before going any further, let’s consider a practical ques- 230 Chapter 13 Testing Your GWT Application 5. Martin Fowler’s page on Refactoring at www.refactoring.com/ is a must-see. Download from www.wowebook.com ptg tion: what would you do if a bug surfaces in your code? (Yes, even though you may test and test and test, bugs almost surely remain in your code.) No matter how tempted you may be, what you won’t do is run away to patch your source code: There’s a more important problem to fix: namely, that your test code isn’t obviously good enough, for it let a bug go past! The first step, after you understand how the error came to be, is to write a new test case that will simulate the situation that led to the error, and thus logically fail. Then, and only then, try to fix the code and run the test suite again; your recently added test should now pass, but more important, you now have better tests for the future. It is obvious that your original tests missed some conditions; your new ones at least take care of one prob- lem, and that kind of bug won’t reappear. Unit Testing with JUnit Many Java programmers already use JUnit for testing their code, but what’s most inter- esting is the fact that you can also use it to test GWT code, despite its compilation into JavaScript. JUnit6 is a member of the global xUnit family, whose members allow automatic testing for code written in languages as diverse as Smalltalk (SUnit), PHP (PHPUnit), HTTPUnit (web pages and services), and JSUnit (JavaScript). Its current version is 4.8.1, and you’ll have to get the required jar file, as we mentioned in Chapter 2, “Getting Started with GWT 2.” With earlier versions of GWT, you had to create the appropriate project structure with the jUnitCreator command-line tool, but now the required directories and files are automatically created with the project.7 The standard structure has a test directory at the same level as src. You can parallel the project structure from src/.../client into a test/.../client directory; having the test code and the tested code in the same package will help with visibility matters, but won’t hamper creating the application, because by default only code in src/client gets compiled. A Basic JUnit Example So as to get in gear for more complicated testing, let’s at first consider how to test the KeyValueMap class we used to store parameters for forms in Chapter 4, “Working with Browsers.” (Don’t fret if this example doesn’t have anything to do directly with forms; we’ll presently get to testing such code.) As that class is at src/com/fkereki/mvppro- ject/client/KeyValueMap.java, our test code will reside at test/com/fkereki/ mvpproject/client/KeyValueMapTest.java; note the usual naming convention for our test class. For ease of understanding, let’s review the original class code. 231Unit Testing with JUnit 6. See http://junit.org/. 7. At least, when you use the Eclipse plugin. If you use webAppCreator, you’ll have to pass the -junit parameter to it. Download from www.wowebook.com ptg package com.fkereki.mvpproject.client; // ...imports... public class KeyValueMap extends HashMap { private static final long serialVersionUID = 5225712868559413562L; public KeyValueMap() { this(""); } public KeyValueMap(final String params) { initializeWithString(params); } public final void initializeWithString(final String params) { clear(); if ((params != null) && !params.isEmpty()) { String[] args = params.split("&"); for (String element : args) { int equalIndex = element.indexOf("="); if (equalIndex == -1) { put(element, ""); } else { put(element.substring(0, equalIndex), element .substring(equalIndex + 1)); } } } } @Override public String toString() { String result = ""; String separator = ""; for (String key : keySet()) { result += separator + key + "=" + get(key); separator = "\n"; } return result; } } 232 Chapter 13 Testing Your GWT Application Download from www.wowebook.com ptg To test this class, we might write code such as the following. All the @Test marked methods will be run, but the order cannot be defined. We could use the setUp(...) and tearDown(...) methods to prepare the environment for the whole set of tests, but we won’t be needing them in this particular example. package com.fkereki.mvpproject.client; // ...imports... public class KeyValueMapTest { @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } A first test would verify whether a KeyValueMap, initialized with an empty string, is empty.8 @Test public void testKeyValueMap() { final KeyValueMap kvm0 = new KeyValueMap(""); assertTrue(kvm0.isEmpty()); } We could then move on to testing KeyValueMap objects, initialized with one or more key=value pairs, to check if they have the correct sizes and contents.9 @Test public void testKeyValueMapString() { final KeyValueMap kvm1 = new KeyValueMap("lincoln=1865"); assertEquals(1, kvm1.size()); final KeyValueMap kvm2 = new KeyValueMap("lincoln=1865&darwin=1882"); assertEquals(2, kvm2.size()); assertTrue(kvm2.containsKey("lincoln")); 233Unit Testing with JUnit 8. Some people prefer naming the test so it says what it will test (and possibly even what results it would expect) such as testKeyValueMapWithEmptyString(...) and testKeyValueMapWithEmptyStringShouldReturnEmpty(...)—I go for shorter names, but to each his own poison! 9. There are two Assert classes; see http://stackoverflow.com/questions/291003/differences- between-2-junit-assert-classes for more on picking one. Download from www.wowebook.com ptg assertTrue(kvm2.containsKey("darwin")); assertEquals("1865", kvm2.get("lincoln")); assertEquals("1882", kvm2.get("darwin")); } The toString(...) method also requires some tests. Note that we don’t check the complete string, because we cannot be sure about in which order the keys will be returned. (And, in fact, we do not actually care about the order.) Be careful not to write “fragile” tests, which fail even when we haven’t changed anything that should impact them.10 @Test public void testToString() { final KeyValueMap kvm0 = new KeyValueMap(""); assertEquals("", kvm0.toString()); final KeyValueMap kvm1 = new KeyValueMap("lincoln=1865"); assertEquals("lincoln=1865", kvm1.toString()); final KeyValueMap kvm2 = new KeyValueMap( "lincoln=1865&darwin=1882&einstein=1955"); final String kvmst2 = kvm2.toString(); assertTrue(kvmst2.contains("lincoln=1865")); assertTrue(kvmst2.contains("darwin=1882")); assertTrue(kvmst2.contains("einstein=1955")); assertTrue(kvmst2.contains("\n")); } } Running the code (with Eclipse, select the test directory, right-click it, and select Run As, and then JUnit Test) produces the much desired green bar that shows all tests ran as hoped. (See Figure 13.1.) If you had more tests in the directory, they would have been executed as well. You can create a suite of your own,11 but the automatic way (running all @Test annotated methods) is easier. If you changed, say, the first lines in testKeyValueMapString to read (note the wrong size check) as follows: public void testKeyValueMapString() { final KeyValueMap kvm1 = new KeyValueMap("lincoln=1865"); assertEquals(5, kvm1.size()); then the test run would have produced a different result, a red bar, and an explanation of the failed test, showing what was expected and what was actually seen. See Figure 13.2 for such a failure. 234 Chapter 13 Testing Your GWT Application 10. See http://xunitpatterns.com/Fragile%20Test.html for a discussion on this; in particular, the “Sensitive Equality” section directly applies to our example. 11. See http://junit.org/apidocs/junit/framework/TestSuite.html for an explanation of Test Suites. Download from www.wowebook.com ptg Figure 13.1 If all tests run as desired, you’ll get a green bar, plus a report showing which tests were run, and some timing details. 235Unit Testing with JUnit Figure 13.2 A wrong test produces a red bar instead and an explanation of the failed condition. Other JUnit methods of interest (and the names are quite self-documented) include assertArrayEquals(...), assertNull(...) and assertNotNull(...), assertSame(...) and assertNotSame(...), and assertTrue(...) and Download from www.wowebook.com ptg assertFalse(...), among others. If you wanted to test an exception, you could use fail(...) as per the following pattern.12 Try { // do something that should raise an exception fail(); } catch (TheExpectedException e) { // OK, this should be the case } catch (Exception e) { // oops, an unexpected exception fail(); } If the code doesn’t produce the expected exception, or if it produces an unexpected one, the fail(...) calls will produce a red bar. However, a question pops up: How thorough was our test? Let’s get to this before moving on to more complex cases. Test Coverage with Emma There are several measures of to which degree you have tested your code, and the most basic one is called Statement Coverage, which answers the question “Has every sentence in the program been executed at least once?” Statement coverage is presented as a per- centage, from 0% (you haven’t actually tested anything!) to 100% (all the parts in your code have been exercised at least once.)13 For Eclipse GWT programming, you can use the EclEmma plugin, which produces a visual map showing with colors what parts of your tested code were or weren’t executed, after any test run.14 Let’s apply it to the test we just developed; was it complete, did we miss anything? After installing the plugin, a new option appears in the menu. Select your test directory, right-click it, and pick Coverage As, JUnit Test, and you’ll get a report showing how much of your code was run, and a view of the source code highlighting in green the executed parts, and in the red the missed ones. See Figure 13.3. 15 236 Chapter 13 Testing Your GWT Application 12. In JUnit 4 (but not with GWTTestCase) you could also write @Test(expected= TheExpectedException) and do away with the try...catch block. 13. Though this is not a sufficient condition to ensure testing thoroughness, it should be clear that if your tests haven’t managed to execute a given sentence, that particular sentence could actually be anything, and you wouldn’t notice. Not having 100% coverage permits errors; however, don’t read this as “100% coverage guarantees no errors”! 14. See www.eclemma.org for more on this. 15. Note that depending on the current versions of the plugin, you might have to patch it according to instructions on http://code.google.com/webtoolkit/doc/latest/DevGuideTestingCoverage.html. This may change in the future, so check it out carefully before proceeding. Download from www.wowebook.com ptg Figure 13.3 A statement coverage run shows that our tests actually missed trying out two parts of the code, the empty KeyValueMap() constructor and the first put(...) call in the initializeWithString(...) method. EclEmma noted that not all the KeyValueMap code had been tested, and the result is correct: We missed trying out the basic parameter-less constructor, and we also didn’t try initializing an object with a string such as curly&larry&moe&stooges=3, which would store empty values for the first three keys. We can fix this by adding a few more tests, such as the following. @Test public void testKeyValueMapNoParameters() { final KeyValueMap kvm = new KeyValueMap(); assertTrue(kvm.isEmpty()); } 237Unit Testing with JUnit Download from www.wowebook.com ptg @Test public void testKeyValueMapNoValues() { final KeyValueMap kvm = new KeyValueMap("curly&larry&moe&stooges=3"); assertEquals(4, kvm.size()); assertTrue(kvm.containsKey("curly")); assertTrue(kvm.containsKey("larry")); assertTrue(kvm.containsKey("moe")); assertTrue(kvm.containsKey("larry")); assertEquals("", kvm.get("curly")); assertEquals("", kvm.get("larry")); assertEquals("", kvm.get("moe")); assertEquals("3", kvm.get("stooges")); } Rerunning the coverage test now produces a round 100% score for our class, so we can be more satisfied about our testing. However, note that while low coverage values do point to inadequate testing, high coverage values do not guarantee the absence of errors and are probably not cost-effective. (For example, trying to produce all possible runtime exceptions can be a hard job, and direct code examination may prove to be better.) Usually, values around 80% to 90% are expected, but you shouldn’t go overboard with this; when tests start getting complicated, try to focus more on coding more clearly and finding bugs than on achieving higher coverage values.16 What we have seen up to now is standard fare for JUnit programmers, and it’s easy to see how it would apply to server-side code, or to such methods as previously shown, which do not deal with users, input fields, graphic objects, and the like. How would we apply JUnit to such code? Let’s turn to MVP code, which is far more interesting. Testing MVP Code It might not be obvious at the beginning, but there are many reasons why you couldn’t directly test GWT form code with JUnit. First, JUnit depends on reflection, and GWT compiled code doesn’t provide it, so that’s a showstopper. If your code runs JavaScript (maybe through JSNI, as in Chapter 8, “Mixing in JavaScript,” or by using some DOM or History methods, as in Chapter 4) then JUnit won’t be able to deal with it; it can work only with stand-alone Java code, which cannot run JavaScript. Using any DOM- related methods is also a no-no; JUnit doesn’t have access to a browser, so that code couldn’t even be executed. Finally, using GWT.Create(...) also won’t work, leaving out 238 Chapter 13 Testing Your GWT Application 16. See www.bullseye.com/minimum.html for a good study on this and for actual requirements for several industries. Download from www.wowebook.com ptg RPC and many other GWT features.17 So, how do you get to test any forms with JUnit? We already saw the answer in Chapter 5, “Programming the User Interface”; MVP lets you separate concerns by testing the important Presenter code (based on pure Java) and leaving for later the mostly trivial View code. Testing with Mock Objects You can get around all the preceding limitations if your code is 100% functional, with no side effects, and deals with the rest of the system only through its input parameters. We are going to use a well-known technique and use mock objects to simulate and verify all the interactions between the tested object and the rest of the system. Going back to an already mentioned example, how would you test a method that shows a Window.alert(...) message, in a fully automatic way, with no other tools but Java? A method that produces such an alert is breaking the stated rule given: It is access- ing and using something (namely, Window static methods) that it didn’t receive as a parameter. Another example: How would you test code that sends “tweets,” or that makes online payments? These two examples are even worse; whenever you tested your program, it would start annoying people everywhere with test tweets, or freely making orders and spending your money! If, however, your object under test had received an object, itself with a method that could simulate (but not actually do) the desired behavior, then your code would be testable. (The object under test would use the received object, and the latter could after- ward let us know whether it was called, with which parameters, in what order, and so.) The received object would probably not do any kind of real work (thus, it’s actually a mock object) but rather “keep score” so we can later see what happened. Of course, writing many of these mock objects would be quite a chore. Moreover, if we need to run several different tests on the same object, then we would probably need several sets of the same mock objects; that much programming would more likely make you do without testing! There are several kinds of objects used in testing other than mocks, according to the usual Software Engineering literature; let’s briefly consider some of those, in growing scale of complexity.18 239Unit Testing with JUnit 17. This isn’t actually 100% true; there are some cases that can be tested despite what was asserted in the text. If your code can be tested even with a null object (that is, receiving null instead of an actual object) you can surround a GWT.create(...) call with GWTMockUtilities.disarm() and GWTMockUtilities.restore() calls, and no object will be created. Of course, you won’t be able to run all kind of tests if the object is null, but for some restricted tests it could be a good aid. 18. Sometimes, all these objects are generically called “test doubles,” for they replace real objects in the same way as a “stunt double” replaces an actor for some scenes in a movie. Download from www.wowebook.com ptg n Dummy objects are passed around only to fill parameter lists but never are actually used. n Stubs provide “canned” answers to calls. The possibilities range from constant answers (the method always returns the same value) to matching the received value to a predefined list to decide what value to return. n Fake objects actually do work, but usually with some restrictions, such as using RAM for storing data instead of a database. Developing these objects can be as hard as developing the actual classes themselves. What we are dealing with, mock objects, are objects preprogrammed with expecta- tions specifying the calls that are to be made upon them, and the values they should return. For our tests, both stubs and mock objects could do as well, but in the first case we must do the verifications (was the method called? What values did it receive?) by ourselves, whereas in the second case, the mock library takes care of that. Just for the sake of making the difference clear, we’ll use both stubs and mocks in our MVP tests. EasyMock EasyMock can construct objects for interfaces by using Java’s proxy mechanism. It can also work with objects through a special class extension. (We are working with classes, such as Environment, so we shall at least part of the time require this extension.) Because of how expectations are recorded, it’s likely that refactoring your code won’t affect your EasyMock based tests, which is a good advantage.19 Let’s test a login presenter, the last one we built before using UiBinder. (UiBinder itself neither helps nor hampers our testing, because it’s View-oriented, and we are test- ing Presenter code.) We last saw this in Chapter 5; let’s remember what our Presenter code looked like: package com.fkereki.mvpproject.client.login2; // ...imports... public class LoginFormPresenter extends Presenter { static String PLACE = "login"; LoginServiceAsync loginService; SimpleCallback loginSuccessCallback; 240 Chapter 13 Testing Your GWT Application 19. Some other similar mocking libraries (which even use a similar way for specifying expectations) are Mockito (at http://mockito.org/) and jMock (at www.jmock.org/ ). As usual, “you makes your choices, and you takes your chances.” Download from www.wowebook.com ptg public LoginFormPresenter( final String params, final LoginFormDisplay loginDisplay, final Environment environment, final SimpleCallback callback) { super(params, loginDisplay, environment); loginSuccessCallback = callback; loginService = getEnvironment().getModel().getRemoteLoginService(); final SimpleCallback commonBlurHandler = new SimpleCallback() { @Override public void goBack(final Object result) { final String name = LoginFormPresenter.this.getDisplay() .getName(); final String pass = LoginFormPresenter.this.getDisplay() .getPassword(); final boolean canLogin = !(name.isEmpty()) & !(pass.isEmpty()); (LoginFormPresenter.this.getDisplay()) .enableLoginButton(canLogin); } }; loginDisplay.setNameBlurCallback(commonBlurHandler); loginDisplay.setPasswordBlurCallback(commonBlurHandler); loginDisplay.setName("federico"); loginDisplay.setPassword(""); commonBlurHandler.goBack(null); loginDisplay.setLoginCallback(new SimpleCallback() { @Override public void goBack(final Object result) { final String name = LoginFormPresenter.this.getDisplay() .getName(); final String pass = LoginFormPresenter.this.getDisplay() .getPassword(); LoginFormPresenter.this.getDisplay().enableLoginButton(false); loginService.getSomething(name, pass, new AsyncCallback() { public void onFailure(final Throwable caught) { LoginFormPresenter.this.getEnvironment().showAlert( "Failed login"); 241Unit Testing with JUnit Download from www.wowebook.com ptg LoginFormPresenter.this.getDisplay().enableLoginButton( true); loginSuccessCallback.onFailure(new Throwable()); } public void onSuccess(final String result) { loginSuccessCallback.goBack(result); } }); } }); } } As is, our code is good for testing, because n It interacts with a remote servlet to do the login but gets the needed reference by means of the provided Environment parameter rather than directly accessing an actual server. n It shows some alerts, but it also does it through the received Environment parame- ter, allowing us to check whether alerts were produced but without stopping the test waiting for user interaction. n It works with the View in MVP fashion, but it doesn’t directly deal with widgets or handlers, rather going through the View interface. Furthermore, the View is received as a parameter by the Presenter constructor, which is also good (because the actual View interacts with the DOM and with JavaScript code, which would prohibit the kind of tests we are striving for). We will test the Presenter by providing several mock objects; for example, one such object will simulate connecting to the server and trying a login, a second will make do as if it had shown an alert, whereas yet a third one will stand for the View, and fake all calls from the Presenter and also invoke all its blur and click methods. Just for the sake of showing different ways of working, we shall mock most of the objects with EasyMock but use a stub for the remote login and the successful log callback. We shall do a simple login attempt test. We shall simulate that the user enters a pass- word, then clicks on the login button, and the service will approve the attempt. We are going to implement our own mock login service object, which will use a wasCalled variable to record that the service was truly called, and two more variables (calledName and calledPass) to store the received values, so we can later test what they actually were. Boolean variable didReturn will be used to check whether the Presenter did or didn’t call the successful login callback.20 242 Chapter 13 Testing Your GWT Application 20. The name LoginFormPresenter2Test has to do with our testing the second version of the LoginFormPresenter, from Chapter 5. Download from www.wowebook.com ptg package com.fkereki.mvpproject.client.login2; // ...imports... public class LoginFormPresenter2Test { boolean didReturn = false; boolean wasCalled = false; String calledName = ""; String calledPass = ""; Now, let’s write our test. We shall be using EasyMock objects for the Environment, for the Model that the Environment will be asked to return, and for the View; we create all these with createMock(...). If we cared for the strict sequence of calls (more on this later) we would have rather used createStrictMock(...), and if we didn’t care to provide return values for all calls and could stand returning standard values (zeroes, nulls, and falses), createNiceMock(...) would have been appropriate. @Test public void testLoginPresenter1() { final Environment environmentMock = createMock(Environment.class); final Model modelMock = createMock(Model.class); final LoginFormDisplay loginViewMock = createMock(LoginFormDisplay.class); As promised, we’ll create the login service mock object by hand, just to show it can be done—and that it’s probably more work than needed! For example, we must code otherwise unneeded methods (which will fail(...) if called, so we’ll notice the unex- pected call). We must also record whether we were called, and fail the test if the login service happens to be called more than once, because in this particular test we are assuming the login will be successful. final LoginServiceAsync loginServiceMock = new LoginServiceAsync() { @Override public void changePassword( final String name, final String encryptedNewPassword, final String nonce, final String parametersHash, final AsyncCallback callback) { fail(); // this shouldn't be called! } 243Unit Testing with JUnit Download from www.wowebook.com ptg @Override public void getSessionKey( final String name, final String nonce, final String passHash, final AsyncCallback callback) { fail(); // this shouldn't be called! } @Override public void getSomething( final String name, final String pass, final AsyncCallback callback) { if (wasCalled) { fail(); } else { wasCalled = true; calledName = name; calledPass = pass; } } }; Now, let’s start setting our expectations. From what we know about the Presenter, it shall require getting a reference to the underlying Model object; our Environment must expect a getModel(...) call and return modelMock. Likewise, modelMock must expect a call asking for the remote login service object and must return our hand-build loginServiceMock. If any of these calls fails to happen, or happens more than once, the test will fail. expect(environmentMock.getModel()).andReturn(modelMock); expect(modelMock.getRemoteLoginService()).andReturn( loginServiceMock); The Presenter will also call setNameBlurCallback(...) and similar methods so the View will know what to call on blur and click events. We must store whatever values are provided for callbacks, to use them later when needed. EasyMock provides the Capture<...> class and capture(...) methods to simplify this oft-required task. When the corresponding methods are invoked, the received parameters will get stored. final Capture> nameBlurCapture = new Capture>(); 244 Chapter 13 Testing Your GWT Application Download from www.wowebook.com ptg final Capture> passwordBlurCapture = new Capture>(); final Capture> callbackCapture = new Capture>(); loginViewMock .setNameBlurCallback(EasyMock.capture(nameBlurCapture)); loginViewMock.setPasswordBlurCallback(EasyMock .capture(passwordBlurCapture)); loginViewMock.setLoginCallback(EasyMock.capture(callbackCapture)); Now, from what we know, the Presenter will initialize the name field with "federico" (guess why?) and will empty the password field. The following two lines set up expecta- tions for the mock objects, which will later be verified. loginViewMock.setName("federico"); loginViewMock.setPassword(""); We know that at the beginning, the Presenter will check the values of the name and password fields and therefore decide whether to enable or disable the login button. When the getName(...) method gets called, we want it to return "federico" (because nobody modified it); likewise, getPassword(...) should return an empty string, and thus we should expect the Presenter to disable the login button. If you want to test an exception, you could use andThrow(...) instead of andReturn(...).21 expect(loginViewMock.getName()).andReturn("federico"); expect(loginViewMock.getPassword()).andReturn(""); loginViewMock.enableLoginButton(false); When we construct the Presenter object, we must provide it with a callback, which the Presenter should call when the login succeeds. We could do something along the lines of final SimpleCallback callbackMock = new SimpleCallback() { @Override public void goBack(final String result) { assertFalse(didReturn); didReturn = true; } }; 245Unit Testing with JUnit 21. EasyMock also provides more elaborate matchers, such as anyObject(...), anyString(...), anyInt(...), and so on, which accept any parameter of the said type; notNull(...), which accepts any non-null value; and matches(...), which can be used for pattern matching the received value. You can use these matchers on a parameter-by-parameter basis. Download from www.wowebook.com ptg Back to the test expectations, we shall be simulating that the user entered a password, and the password blur method will be fired. We know the presenter will have to check both the name and password fields to decide whether to enable the login button, and this time it will pass a true value. expect(loginViewMock.getName()).andReturn("federico"); expect(loginViewMock.getPassword()).andReturn("eduardo"); loginViewMock.enableLoginButton(true); Now, because all pieces are in place, and all expectations were set, let’s get the ball rolling. The replay(...) method puts all objects in expect/check mode, so whenever they get called, parameters will be tested, values will be returned, all according with our setup. EasyMock.replay(modelMock); EasyMock.replay(loginViewMock); EasyMock.replay(environmentMock); Creating the Presenter object starts the replay. We can right away check whether it provided a non-null callback for clicks on the login button and if it stored the loginServiceMock we provided.22 final LoginFormPresenter lp = new LoginFormPresenter("", loginViewMock, environmentMock, callbackMock); assertTrue(callbackCapture.getValue() != null); assertTrue(lp.loginService != null); assertTrue(lp.loginService == loginServiceMock); Calling the password blur handler simulates the data entry, and calling the login but- ton handler simulates the final click. passwordBlurCapture.getValue().goBack(null); callbackCapture.getValue().goBack(null); Now, all we have to do to finish our tests is check whether our handmade mock objects were actually called as expected. assertTrue(didReturn); assertTrue(wasCalled); assertEquals(calledName, "federico"); assertEquals(calledPass, "eduardo"); } } 246 Chapter 13 Testing Your GWT Application 22. This isn’t that usual; normally, we just test things that are available through a declared inter- face… but why not? Having the test class and the tested class in the same package allows for these kind of “inner checks.” Download from www.wowebook.com ptg All the other checks, such as if a method were called unexpectedly, or with unexpected parameters, are done by EasyMock, so it’s still possible that the test will fail, even if it didn’t produce an error so far. Using EasyMock is a great way to simplify your tests, and gives you enough latitude to provide for the most complicated test situations. (Note, for example, how much shorter and easy to understand were our EasyMock tests than our hand-written login service mock?) If you manage to set up the Presenter tests with EasyMock, the usually most complex parts of your applications will be easily tested, and at full speed, which doesn’t happen with the browser-based tests we shall be considering in the next sections. Integration Testing with GWTTestCase JUnit tests can be applied only to server-side code and to part (hopefully, most) of the client-side code. However, this is not enough; you should test, for example, whether a complex view actually does what it should do, or if a remote service can actually be called and produce the correct results. For all these tests, you need to simulate your actual environment, for the code runs in a browser. GWTTestCase acts like a bridge between JUnit and GWT. Previous versions of GWT used actual browsers to run unit tests, but since 2, HtmlUnit is used as a built-in browser. As HtmlUnit is written in Java, you can debug GWT tests in development mode, which is an advantage. Also, you can test the results of integrating several classes (including classes that require JavaScript) and find problems that might have escaped you if you only did browser-less testing. On the negative side, because it’s not an actual browser, there can be subtle problems; you will have to use Selenium or similar tools for doing tests on actual compiled code. Also, delays are more significant, because a development mode shell has to be started, and the actual runs will also take longer if you communicate with a server. Finally, you won’t be able to mock objects automatically with EasyMock or the like, which require some Java features such as reflection, that aren’t available with GWT; you’ll have to do that work by yourself. Setup is also a bit more complex; you have to include the source directories in the Class path; see Figure 13.4. Let’s study a couple of examples of this kind of testing and check both a View and a Remote Servlet. Testing a View We’ve tested a login presenter; let’s now do some tests of the login view. You could try to do all kinds of tests (layout, rendering, events, callbacks, and more) but remember to think in terms of economy and focus on the most important tests! 247Integration Testing with GWTTestCase Download from www.wowebook.com ptg Figure 13.4 For GWTTestCase testing, you must include the source directories in the Classpath; click on Advanced... then Add Folder to do so. As a reminder, the code to be tested was package com.fkereki.mvpproject.client.login2; // ...imports... public class LoginFormView extends View implements LoginFormDisplay { AsyncCallback loginCallback; AsyncCallback nameBlurCallback; AsyncCallback passwordBlurCallback; final TextBox nameTextBox = new TextBox(); final TextBox passwordTextBox = new PasswordTextBox(); final Button loginButton = new Button("Log in"); final FlexTable flex = new FlexTable(); final DockPanel dock = new DockPanel(); 248 Chapter 13 Testing Your GWT Application Download from www.wowebook.com ptg public LoginFormView() { // add a click handler to the login button // add a blur handler to the name and password fields // set up all fields onscreen and "init" the widget } @Override public void enableLoginButton(final boolean b) { loginButton.setEnabled(b); } @Override public final String getName() { return nameTextBox.getValue(); } @Override public final String getPassword() { return passwordTextBox.getValue(); } @Override public final void setLoginCallback(final SimpleCallback acb) { loginCallback = acb; } @Override public final void setName(final String s) { nameTextBox.setValue(s); } @Override public void setNameBlurCallback(final SimpleCallback acb) { nameBlurCallback = acb; } @Override public final void setPassword(final String s) { passwordTextBox.setValue(s); } @Override public void setPasswordBlurCallback(final SimpleCallback acb) { passwordBlurCallback = acb; } } 249Integration Testing with GWTTestCase Download from www.wowebook.com ptg Our test class must now extend GWTTestCase. Note we use the same package as the original view’s, so we can take advantage of visibility rules. package com.fkereki.mvpproject.client.login2; // ...imports... public class LoginFormViewGWTTest extends GWTTestCase { We use a pair of Boolean variables to learn whether specific callbacks were invoked. (Previously we did a similar thing.) boolean loginWasCalled; boolean blurWasCalled; All GWTTestCase instances must include a getModuleName(...) that just returns the complete name of the unit to be tested. @Override public String getModuleName() { return "com.fkereki.mvpproject.Mvpproject"; } With all this out of the way, let’s start our testing. After creating the View object, we must set its callbacks, but note that they don’t actually do anything, but just record they were called.23 @Test public void testLoginView() { final LoginFormView lv = new LoginFormView(); final SimpleCallback blurCB = new SimpleCallback() { @Override public void goBack(final Object result) { blurWasCalled = true; } }; lv.setNameBlurCallback(blurCB); lv.setPasswordBlurCallback(blurCB); lv.setLoginCallback(new SimpleCallback() { 250 Chapter 13 Testing Your GWT Application 23. GWTTestCase is derived from JUnit 3, so the @Test annotations aren’t actually necessary; instead, test methods should begin with “test.” I have followed both conventions in the text, so when GWTTestCase catches up with JUnit 4, my code will be ready. Download from www.wowebook.com ptg @Override public void goBack(final Object result) { loginWasCalled = true; } }); Now, we can start using the View’s methods and check if they work as expected. The first tests actually use the real fields; you could argue that you shouldn’t get down to this level and access any widgets (but rather work only through interfaces) but in any case you’ll have to, in order to fire click and blur events. Also, there’s no other way to test whether the login button was actually enabled or disabled. lv.nameTextBox.setValue("urk"); lv.passwordTextBox.setValue("ork"); assertEquals("urk", lv.getName()); assertEquals("ork", lv.getPassword()); lv.setName("federico"); lv.setPassword(""); assertEquals("federico", lv.getName()); assertEquals("", lv.getPassword()); lv.enableLoginButton(false); assertFalse(lv.loginButton.isEnabled()); lv.enableLoginButton(true); assertTrue(lv.loginButton.isEnabled()); Testing events require some DOM manipulation. Note the DomEvent.fireNativeEvent(...) method, which is quite interesting. final Document doc = com.google.gwt.dom.client.Document.get(); final NativeEvent evt1 = doc.createBlurEvent(); DomEvent.fireNativeEvent(evt1, lv.nameTextBox); assertTrue(blurWasCalled); blurWasCalled = false; DomEvent.fireNativeEvent(evt1, lv.passwordTextBox); assertTrue(blurWasCalled); final NativeEvent evt2 = doc.createClickEvent(0, 0, 0, 0, 0, false, false, false, false); DomEvent.fireNativeEvent(evt2, lv.loginButton); assertTrue(loginWasCalled); } } If wanted, you could have linked the view to an actual presenter and provided an actual Model to work with, and so on. You shouldn’t, however, try to do a full accept- ance test because tools such as Selenium make shorter work out of that. 251Integration Testing with GWTTestCase Download from www.wowebook.com ptg Testing a Servlet Our previous test didn’t require a servlet, but at some time you’ll have to write such tests. (Otherwise, how would you detect mismatched parameters, a wrong setup, or any other such problems?) The main problem here is the need for a wait, because RPC works asynchronously. If you just did a RPC and then went on to test results, you would fail, because they wouldn’t have arrived. Let’s test a pair of methods in the World servlet. Note that for this, you must have preloaded the database with countries, states, and cities (as we saw in Chapter 6, “Communicating with Your Server”). You should probably be running your own data- base; you wouldn’t want to run tests against production data, would you? Also, you’ll have to modify the gwt.xml file a bit to include the path to the servlet. The start of our test class is similar to the one we just saw for a view. package com.fkereki.mvpproject.client.rpc; //...imports... public class WorldServiceGWTTest extends GWTTestCase { final Model model = new Model(); @Override public String getModuleName() { return "com.fkereki.mvpproject.Mvpproject"; } As a minimal test, we could try getting back all cities in Soriano, a department (region code 17) in Uruguay (country code UY) and verify whether “Darwin” is included in the list. (We already saw this city in Chapter 6; also see Figure 13.6 farther on in this chapter.) Although this test is clearly not enough, the most important part is the delayTestFinish(...) call, so the servlet will have time to return, and the finishTest(...) call after the servlet callback. Also, note that the result tests must be verified in the callback. @Test public void testGetCities() { model.getCities("UY", "17", 0, 1000, new SimpleCallback>() { @Override public void goBack( final LinkedHashMap result) { 252 Chapter 13 Testing Your GWT Application Download from www.wowebook.com ptg assertTrue(result.size() > 0); assertEquals("Darwin", result.get("darwin").cityAccentedName); finishTest(); } }); delayTestFinish(5000); // 5 seconds } The test for countries is almost the same. @Test public void testGetCountries() { model .getCountries(new SimpleCallback>() { @Override public void goBack(final LinkedHashMap result) { assertTrue(result.size() > 0); assertTrue(result.containsKey("UY")); finishTest(); } }); delayTestFinish(5000); } } Because of the servlet delays, you shouldn’t do more than one call at once. (In some freak situations, the second call might finish before the first one, and results would be totally unexpected.) If you need do a second RPC, include it in the callback for the first call. To wrap this up, let’s verify what I said about the delays. (See Figure 13.5.) Running these tests took about 49 seconds; the greater part of that was internal GWT setup work. It’s a fact that with GWTTestCase you can test parts and interactions of your code that you would miss otherwise, but because of the longer times, do try to test as much of the code with pure JUnit and leave special tests for GWTTestCase. Acceptance Testing with Selenium With the tools we have already seen, it should be clear that you could test most of your application, if not all (file uploads can get tricky, for example), using a mixture of JUnit (for server-side code, and for nonvisual parts of client-side code) and GWTTestCase (for the visual parts of your code). However, let’s give a quick look at an oft-used tool, Selenium, that helps writing and running tests for a complete web application. Though we won’t be getting in full detail (Selenium is quite vast, with dozens of commands) we’ll show how to use it and some caveats you should take into account. 253Acceptance Testing with Selenium Download from www.wowebook.com ptg Figure 13.5 GWTTestCase testing usually requires much longer setup times, though the tests themselves may be fast. Three different software pieces compose Selenium, but we’ll mostly consider the first one, which lets you create and run tests individually; however, you’ll probably sooner or later decide that the other two tools, which have to do with automating large scale tests on different browsers, are also of interest. n Selenium IDE is a Firefox add-on that provides a simple interface for building and running tests, either on its own or as part of complete test suites. You can record your actions and checks, and store them as a script that can be played back. If you want, you can even write your tests from scratch (the test language is actually quite simple) but the IDE helps a lot. n Selenium RC (Remote Control) enables you to use Java and other programming languages for extending your tests. Selenium IDE can run only static (i.e., prede- fined) tests; if you want to iterate through a result set or do arithmetic or other tests, you can program them in Java while using Selenium commands to get the data, for example. Using a programming language would also allow setting up a complex environment before running the actual tests. Also, Selenium RC enables you to run tests on browsers that are not supported by the IDE. Thus, you get to use Selenium as a “starter kit,” and then do the rest of your work in Java, with all its development tools. n Selenium Grid enables you to run several instances of Selenium RC at the same time, even under different operating systems and with different browsers. This is more useful for large series of tests, which can be run in parallel. You should use Selenium only on the compiled application (see Chapter 15, “Deploying Your Application”) as the last (“acceptance”) kind of test. Reading between lines, this already suggests a first consideration; you shouldn’t probably start with 254 Chapter 13 Testing Your GWT Application Download from www.wowebook.com ptg Selenium until you are fairly confident that the user interface won’t be changing too much; otherwise, you might have to redo the completed tests almost from scratch. A Very Simple Example Let’s remember our City Browsing form. If we browsed to “Uruguay,” and picked “Soriano” department, the first 20 cities would be as shown in Figure 13.6. 255Acceptance Testing with Selenium Figure 13.6 The cities browsing form will be tested with Selenium. Given the data we loaded, a possible test would be n Open the correct URL. n Wait for the country data to come in. n Click on it, and select Uruguay. n Wait again for the states data. n Click it, and select Soriano. n Click the First 20 Cities button. Download from www.wowebook.com ptg n Wait for the table to get populated. n Check that “darwin” appeared in the list. You can use Selenium to record these actions and tests, and if you opt to run the test, you’ll get a green result. (See Figure 13.7.) You can save this test, and later run it as a part of a general suite. 256 Chapter 13 Testing Your GWT Application Figure 13.7 Running our short test produces a green result; everything checked out okay. Note that you had little to type or specify to create and run this test; you could also have managed with GWTTestCase, but this is easier to set up, and furthermore you can run it in different browsers and under different operating systems. There are far more options available for testing, including the possibility of writing your own tests and wait conditions in JavaScript, so you could, for example, test if the list of Uruguayan departments is 19 elements long, or whether the United States shows 50 states. Check the Selenium documentation for a complete list of commands.24 24. You can find it at http://seleniumhq.org/docs/04_selenese_commands.html#chapter04-reference. Download from www.wowebook.com ptg What Can Go Wrong? We just scratched the surface of Selenium with our previous example, but let’s anyway consider several problems that you might find. The most important point is that the logic Selenium uses to decide what locator to use for a given screen object frequently causes problems, because simple or trivial changes in the form may cause Selenium to refer to a different field. The best solution is to assign your own IDs with DOM.setElementAttribute(someWidget.getElement(), "id", "theNewId"); and use it as the locator.25 However, note that this can quickly become tiresome.26 GWT applications are (by definition) heavy on DHTML and Ajax, meaning that most of the time, the interface gets created dynamically, depending on data received from the server. You should get used to including waitFor... Selenium commands, because otherwise your tests will run too fast, missing widgets or values it should find. As a plus, you can add extra functionality to Selenium by either writing your own extensions, or by using already developed ones.27 (You can even do unstructured testing with goto statements. How about that, in modern times?) Other extensions help work- ing with frames and windows, so even if you at first cannot run the tests you want, you can probably manage by means of extending Selenium, or using Selenium RC. Summary We have seen three different methods for automatic testing of GWT applications: a spe- cific one, GWTTestCase, and two generic ones: JUnit and Selenium. Although you won’t be able to use Selenium until your application is stable (which, on the other hand, isn’t such a problem because Selenium is geared toward acceptance tests, and you don’t run those if the application is only halfway there!) the complete trio of tools do provide a strong base for your tests. 257Summary 25. An alternative: Consider using the ensureDebugId(...) method; read http://google-web- toolkit.googlecode.com/svn/javadoc/2.0/index.html?com/google/gwt/user/client/ui/UIObject.html for more on this. 26. Unhappily, you won’t be able to assign the id attribute with UIBinder. 27. You can find a repository of such extensions at http://wiki.openqa.org/display/SEL/ Contributed+User-Extensions, whereas instructions for writing your own are at http:// release.seleniumhq.org/selenium-core/1.0/reference.html#extending-selenium. Download from www.wowebook.com ptg This page intentionally left blank Download from www.wowebook.com ptg 14 Optimizing for Application Speed You can never have a too speedy application, and in this chapter we’ll consider design patterns that let you take advantage of spare time, and measuring tools that let you find out where your application is spending most of its processing time. The combination of both these programming techniques and benchmarking tools will enable you to optimize your application in often surprising ways. We are going to consider several ways to speed up your application. We’ll start with some design patterns that will enable your application to use background processing to provide or process data more quickly. We’ll also be including the new GWT 2 bundles, which let you save time by sending several kinds of data from the server to the client in a single trip. And finally, as more general solutions, we’ll also introduce several bench- marking tools that will let you detect bottlenecks in your application. Design Patterns for Speed Let’s start by stating our goals clearly. We want to consider design patterns that will allow greater speed from the user’s point of view. In truth, we are not saying the patterns will make the application faster, but at least that it will seem faster to the user; an important difference! As always, it will be a matter of trade-offs; we use some extra (client) memory, but we can skip doing repeated calls to the server, or we speculate and get some data from the server just in case the user wants it. Given our preceding definition, the prevalidation pattern (that we saw in Chapter 6, “Communicating with Your Server”) could equally as well have been included here, because it allows faster feedback and better usability. Yet again the same could be said of the code splitting feature we’ll see in the next chapter, for it allows a much faster load process, that is balanced, to be sure, with some delays down the line. Download from www.wowebook.com ptg Caching GWT usually makes your application perform better, but there is one area in which it can actually make performance worse: caching. Whenever your browser requests data (such as a page) from a server, it first looks into its own cache, to check whether it already has the data. If the required results are found in memory, the browser will forego calling the server and just provide the data from the cache. (Of course, several conditions must be fulfilled before something is kept in the cache, but that’s not relevant here.) The problem here is that whenever GWT calls a remote servlet, it implements the RPC by means of a noncacheable Ajax call, so even if your program asks for the same data over and over again, the browser won’t store it in its cache and will require it anew from the server every time. If your application requires the same (constant) data that it has already received earlier, you could enhance performance by setting up a local cache of your own, bypassing the browser’s one. The pattern of usage would require, before calling the server, to check if the asked-for data is already in the cache, and if so, just skip the call. There are two points to consider: n Never use a cache for information that changes often. n If you are using a cache for information that can go stale over time, consider using a timestamp so that you’ll avoid using old data.1 The general pattern for using a cache would be as follows. Class_with_cache: Define class attributes for the cache Set the cache to empty initially Whenever data are required: Check if the asked for data are already in the cache If so, Get the data from the cache, and return it Otherwise, Use RPC, Ajax, or whatever, to get the data On callback: Put the data in the cache, and return it Let’s illustrate this pattern with our CountryState object, which we used in our cities browsing form in Chapter 6. The simplest application of this pattern has to do with the countryCode ListBox. In this case, we can make do with a static object in which to store the received country list; whenever we ask for the countries list, we first check if the list is already loaded, and if so, we just return it. Changes in the Model class code are minimal with regard to the code we saw earlier in the book. (In Chapter 6 we had mentioned that it would be better if no other part of the application knew the actual 260 Chapter 14 Optimizing for Application Speed 1. This is, in essence, how browsers manage their own internal caches. Download from www.wowebook.com ptg details of how to connect to any service. Using methods such as getCountries(...) effectively encapsulates those details and furthermore allows optimizations as we shall see.) static LinkedHashMap countriesCache = null; public void getCountries( final AsyncCallback> cb) { if (countriesCache == null) { getRemoteWorldService().getCountries( new AsyncCallback>() { @Override public void onFailure(final Throwable caught) { // ...error... } @Override public void onSuccess( final LinkedHashMap result) { countriesCache = result; cb.onSuccess(result); } }); } else { cb.onSuccess(countriesCache); } } The countries cache is null at the beginning. The first time you call getCountries(...), the resulting list will be stored in the cache, and all further calls will just produce the data from there, with no delays. Working with states is just a tad more complex, because we’ll have to use some kind of collection; storing just the states from a single country wouldn’t do. Whenever we get asked for the states of a given country, we must check if statesCache already contains that country as a key; if so, we already got the data and don’t have to go to the server. This code is also in the Model. static LinkedHashMap> statesCache = new LinkedHashMap>(); public void getStates( final String country, final AsyncCallback> cb) { if (!statesCache.containsKey(country)) { getRemoteWorldService().getStates(country, new AsyncCallback>() { 261Design Patterns for Speed Download from www.wowebook.com ptg @Override public void onFailure(final Throwable caught) { // ...error... } @Override public void onSuccess( final LinkedHashMap result) { statesCache.put(country, result); cb.onSuccess(result); } }); } else { cb.onSuccess(statesCache.get(country)); } } If you are worried about total RAM requirements, you could apply some kind of LRU (least recently used) logic and just store states for the more recently used, say, 10 or 20 countries. The same kind of change could be applied to the cities fetching logic, and the changes are relatively minor. The only point deserving attention is the way we build up the cache key by concatenating the country, state, and start position parameters, separated by colons.2 /* * The cache key is COUNTRY:REGION:STARTING_CITY_NUMBER and the associated * value is a LinkedHashMap */ static LinkedHashMap> citiesCache = new LinkedHashMap>(); The rest of the code follows the same pattern we used earlier. Let’s include it so we can add more changes to it in the following section. public void getCities( final String country, final String state, final int pStart, final int pCount, final AsyncCallback> cb) { if (!country.isEmpty() && !state.isEmpty()) { 262 Chapter 14 Optimizing for Application Speed 2. We are assuming here that pCount is always constant. If this weren’t true, handling the cache would just be a tad harder, but we need not care about the details for this example. Download from www.wowebook.com ptg if (!citiesCache .containsKey(country + ":" + state + ":" + pStart)) { getRemoteWorldService().getCities(country, state, pStart, pCount, new AsyncCallback>() { // ...onFailure() definition... public void onSuccess( final LinkedHashMap result) { citiesCache.put(country + ":" + state + ":" + pStart, result); cb.onSuccess(result); } }); } else { cb.onSuccess(citiesCache.get(country + ":" + state + ":" + pStart)); } } } Now, we’ll study other patterns that we could apply in that case, to speed up sequences of consecutive calls. Prefetching Whenever your client-side application is going to need lots of data from the server, you’ll have to implement some kind of chunking or paging, and that means the user will have to wait for each new “batch” of data. If you could foretell which data a user was going to require, you could use Ajax mechanisms and “get ahead of the game,” getting the data from the server before actually required, and thus providing a much more responsive interface. Of course, you cannot always guess what the user will finally ask for, so you must take into account the possibility that you’ll guess wrongly and get some unneeded data; that is a certain risk that you must balance with the sure delays that the user will have to endure if you opt to not prefetch. Prefetching just means trying to anticipate your user needs and call for data before the user actually requires it. However, you must be careful with this pattern: If you just go overboard by prefetching everything in sight, you’ll just make things worse! Since the times of limited-speed, dial-up modems, all browsers have limited the maximum allowed number of client-to-server connections. (As a matter of fact, that limitation even got included as part of the HTTP version 1.1 standard, which reads “A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.”) If you make 263Design Patterns for Speed Download from www.wowebook.com ptg several requests for data to your server, you should assume that at most only two of them will go out (in parallel) while the rest will be queued for even longer-than-usual delays.3 The simplest pattern for prefetching is as follows: Class_with_cache_and_prefetching: Define class attributes for the cache Set the cache to empty initially Whenever data are required: Check if the asked for data are already in the cache If so, Get the data from the cache, and return it Otherwise, Use RPC, Ajax, or whatever, to get the data and also to prefetch extra data On callback: Store the data in the cache If data were needed (as opposed to prefetched) Return it We can apply this kind of logic to the cities browser application. We will implement a cache along the lines shown in the previous section, but we will also be storing extra data as a prevision for the future. Note in particular that the first check at the cache looks for cities starting at position pStart+pCount; if the user steps through the data consecutively, that will mean that the required data will already be loaded in memory.4 public void getCities( final String country, final String state, final int pStart, final int pCount, final AsyncCallback> cb) { if (!country.isEmpty() && !state.isEmpty()) { if (!citiesCache.containsKey(country + ":" + state + ":" + (pStart + pCount))) { getRemoteWorldService().getCities(country, state, pStart + pCount, pCount, 264 Chapter 14 Optimizing for Application Speed 3. Modern browsers have upped the limit from the original value of two, but the principle remains the same: You shouldn’t hog all communication “just in case,” and should the user be working with a browser that allowed a smaller number of connections than you expected, performance would suffer. 4. Once again, we are assuming pCount never changes. Download from www.wowebook.com ptg new AsyncCallback>() { public void onFailure(final Throwable caught) { // ...error... } public void onSuccess( final LinkedHashMap result) { citiesCache.put(country + ":" + state + ":" + (pStart + pCount), result); } }); } The rest of the code is exactly the same as in the cache-only version, which we saw at the end of the previous section. if (!citiesCache .containsKey(country + ":" + state + ":" + pStart)) { // ...as in the "cache only" version... } } } Note that the code works by doing two calls: the first to prefetch some data and load the cache, and the second to provide the actually asked-for data. Of course, the calls are done only if the involved data aren’t already in the cache. If you foresee the possibility of the user browsing many different countries and states, applying some kind of limits to the cache would prove to be wise; a simple though pos- sibly a bit too extreme solution would be going through the whole cache and simply purging any data coming from a country other than the one the user is currently examining. As is, the user will experiment a certain delay when he calls for the first group of cities, but then, if he just takes a little while before asking for the next page, he will notice no delays at all, for the required data will have already been brought from the server to the client. For even more impressive results, whenever the “states” listbox value changes, you could add a call such as the following, so the first cities (that is, when start is zero) for the given country and state would already be loaded even before the user clicked on the First Cities button. (See the cities browsing Presenter, from where this was taken.) Note that the callback doesn’t do anything!5 265Design Patterns for Speed 5. Why so many get(...) calls? You have to go through the Environment to get at the Model and then ask it to get cities from the server. For the RPC parameters, the Presenter needs the Display to get the Country and State widget, and then get the Country (or State) from it. Download from www.wowebook.com ptg getEnvironment().getModel().getCities( getDisplay().getCountryState().getCountry(), getDisplay().getCountryState().getState(), 0, CitiesBrowserView.CITIES_PAGE_SIZE, new SimpleCallback>() { @Override public void goBack( LinkedHashMap result) { // ...do nothing! } }); If you are feeling even more adventurous, you could wait just a bit before getting the cities, just in case the user changes its mind and picks a different state; you’ll probably want to use a timer, as we will be using in the next section though for different purposes. Thread Simulation Consider any CPU-intensive task, such as parsing and processing large amounts of XML, or displaying lots of data on screen. If a process takes too long, the end user will be shown a message such as Internet Explorer’s “Stop running this script? A script on this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive” or Firefox’s own “A script on this page may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete.” What’s worse, if the user actually pays heed to the warning, he will stop the wayward script, and thus cancel your GWT application! Using threads would be a standard solution for this problem, but GWT won’t allow it, because JavaScript provides just a single thread of execution, implying that compiled threaded code wouldn’t work as expected. Ajax would also provide a solution, if you required server-side processes but is no good for your client-side problem. Luckily, there are two solutions for this problem: You could use timers and parcel your script in several small pieces, or you can go one step better, and use GWT’s own deferred commands, which actually provide even better performance. Let’s apply both of them to showing the cities data in our cities browsing application. A Timer-Based Solution JavaScript provides simple timers, and GWT provides an equivalent Timer class, with a schedule(...) method that works in similar fashion like JavaScript’s setTimeOut(...) method. The idea for this solution is to do just a bit of work and store values so the process can seamlessly continue after a timeout, but freeing the CPU in the meantime. Of course, before resuming the process, check if the process needs to go on; the user might have changed the country or state, or paged forward and backward, and it wouldn’t do if your application ignored that, and kept showing old data. 266 Chapter 14 Optimizing for Application Speed Download from www.wowebook.com ptg The general pattern is as follows: define a class that extends Timer: define attributes so it can save its parameters define attributes so it can save local variables from run to run define attributes so it can save form field values on construction: save the received parameters initialize local variables for the process save the current form field values run() method: if the current form field values match the saved values: execute some process, updating the local variables if there's still more work to be done schedule another process in a short while whenever you want to simulate a thread with a timed method: create an object of the new class above, with appropriate parameters execute its run() method Let’s apply this pattern to our cities browsing example, and in particular, to the logic that actually displays the cities’ data onscreen.6 Most of the code will be left as was, so we shall just highlight the main points. Also (and this supports some things we said about MVP) note that only the Presenter needs any changes. package com.fkereki.mvpproject.client.citiesBrowser3; // ...imports... public class CitiesBrowserPresenter extends Presenter { public static String PLACE = "citybrowse"; We shall implement a Command pattern7 by extending the Timer class. (Note we are referring to the GWT one at com.google.gwt.user.client.Timer and not to the standard java.util.Timer class.) The cities’ display process will be parceled into several short, spaced processes, so the end user will feel the browser to be “more responsive.” private class TimedCitiesDisplay extends Timer { final NumberFormat nf = NumberFormat.getDecimalFormat(); LinkedHashMap citiesList = null; 267Design Patterns for Speed 6. Okay, this particular code is fast enough so you could do without this pattern, but on the other hand, it provides a simple way of showing how to apply this optimization scheme. 7. See http://c2.com/cgi/wiki?CommandPattern for a fuller description. Download from www.wowebook.com ptg Iterator currentCity = null; int currentRow = 0; We shall store the original country, state, and start position, because the user could change to a different country or state, or advance to a different set of cities, and it wouldn’t do to keep displaying the old data. Before we get to display anything, we’ll make sure it matches what the user is seeing onscreen. String originalCountry = null; String originalState = null; int originalStart = 0; This method will be used to display a whole page of cities. We shall be using an itera- tor to step through the list of cities; it will be the link between a part of the process and the next part. We shall also update the three variables we mentioned in the previous paragraph, and display Loading... texts before anything else gets shown. (Of course, this presupposes that displaying just that text is much faster than displaying the whole city data; if not, you would be just exchanging a delay for another!) public TimedCitiesDisplay( final LinkedHashMap pCitiesList) { citiesList = pCitiesList; currentCity = pCitiesList.keySet().iterator(); currentRow = 0; originalCountry = getDisplay().getCountryState().getCountry(); originalState = getDisplay().getCountryState().getState(); originalStart = currentStart; displayEmptyCities(0, "Loading..."); } The run(...) method will display a few cities, and if there are any other remaining ones (see the someMore variable) it will schedule a new process that will continue with the current display. Should there have been any change (different country, state, or start position) the process just won’t do anything, and it won’t schedule a new run either. The CITIES_AT_A_TIME constant defines how many cities will get shown per run (another constant, CITIES_DELAY_IN_MS, is used to specify how many milliseconds apart is a run from the next) and the currentRow variable is used to count the rows, for display purposes. @Override public void run() { boolean someMore = originalCountry.equals( getDisplay().getCountryState().getCountry()) && originalState.equals( getDisplay().getCountryState().getState()) && originalStart == currentStart; 268 Chapter 14 Optimizing for Application Speed Download from www.wowebook.com ptg for (int i = 0; someMore && i < CITIES_AT_A_TIME; i++) { if (currentCity.hasNext()) { final ClientCityData cd = citiesList.get(currentCity.next()); currentRow++; getDisplay().setCityData(currentRow, cd.cityName, nf.format(cd.population), nf.format(cd.latitude), nf.format(cd.longitude)); } else { /* * If there are no more cities, display empty lines * (a fast process) and disable the next timer call */ displayEmptyCities(currentRow, ""); someMore = false; } } /* * If there are still some more cities to display, * schedule a new display process in a short while. */ if (someMore) { schedule(CITIES_DELAY_IN_MS); } } } static final int CITIES_AT_A_TIME = 10; static final int CITIES_DELAY_IN_MS = 250; Here things start getting back to normal, and the rest of the original Presenter code is pretty much unchanged, except for the getAndDisplayCities(...) that now uses our TimedCitiesDisplay class. void getAndDisplayCities() { if (currentStart < 0) { currentStart = 0; } displayEmptyCities(0, "Loading..."); getEnvironment().getModel().getCities( getDisplay().getCountryState().getCountry(), getDisplay().getCountryState().getState(), currentStart, CitiesBrowserView.CITIES_PAGE_SIZE, new SimpleCallback>() { @Override public void goBack( final LinkedHashMap result) { 269Design Patterns for Speed Download from www.wowebook.com ptg new TimedCitiesDisplay(result).run(); } }); } } We can see this solution caught just between runs of our timed process in Figure 14.1. 8 270 Chapter 14 Optimizing for Application Speed 8. A small confession is in order: I had to set CITIES_DELAY_IN_MS to a far larger value so that I could use my screen capture program and get the shown image. Figure 14.1 A part of the cities list has already been shown, and a timer- based command will fire shortly, to continue the display. This is a solution much along the lines of “classic” JavaScript programming, but GWT actually provides an even better alternative, as we shall see in the next section. A Deferred Command-Based Solution Deferred commands are a GWT-specific feature, which make for an even better solution. These commands are queued for execution when the CPU is free, so if you do a small Download from www.wowebook.com ptg part of the process, and use a deferred command to resume processing, GWT will run it as soon as possible, for a better throughput. (With the timer-based solution, if the user isn’t actually doing anything, the time between runs of the display process will simply be wasted.) The general pattern of usage for this solution would be as follows. define a class that extends IncrementalCommand: define attributes so it can save its parameters define attributes so it can save local variables from run to run define attributes so it can save form field values on construction: save the received parameters initialize local variables for the process save the current form field values on execute() method: if the current form field values match the saved values: execute some process, updating the local variables if there's still more work to be done return true, so it will run again shortly afterwards otherwise, return false (the job is done) otherwise, return false (situation changed) whenever you want to simulate a thread with a deferred command: create an object of the new class above, with appropriate parameters use the addCommand() to add your new object to the processing queue Back to the code, once again we’ll leave most of our Presenter unchanged. For the Timer-based solution, note that now we use IncrementalCommand instead. However, if you compare both this version and the one in the previous section, you’ll find more coincidences than differences. package com.fkereki.mvpproject.client.citiesBrowser4; // ...imports... public class CitiesBrowserPresenter extends Presenter { public static String PLACE = "citybrowse"; private class DeferredCitiesDisplay implements IncrementalCommand { final NumberFormat nf = NumberFormat.getDecimalFormat(); LinkedHashMap citiesList = null; Iterator currentCity = null; int currentRow = 0; 271Design Patterns for Speed Download from www.wowebook.com ptg String originalCountry = null; String originalState = null; int originalStart = 0; public DeferredCitiesDisplay( final LinkedHashMap pCitiesList) { citiesList = pCitiesList; currentCity = pCitiesList.keySet().iterator(); currentRow = 0; originalCountry = getDisplay().getCountryState().getCountry(); originalState = getDisplay().getCountryState().getState(); originalStart = currentStart; displayEmptyCities(0, "Loading..."); } Now we have an execute(...) method instead of a run(...) one, but the actual code is pretty much unchanged. Whenever the execute(...) method is run, if it returns true, it means it has to be run again; if it returns false, the command is con- sidered to be done, and execution will cease. public boolean execute() { boolean someMore = originalCountry.equals(getDisplay() .getCountryState().getCountry()) && originalState.equals(getDisplay().getCountryState() .getState()) // && originalStart == currentStart; for (int i = 0; someMore && i < CITIES_AT_A_TIME; i++) { if (currentCity.hasNext()) { final ClientCityData cd = citiesList.get(currentCity.next()); currentRow++; getDisplay().setCityData(currentRow, cd.cityName, nf.format(cd.population), nf.format(cd.latitude), nf.format(cd.longitude)); } else { displayEmptyCities(currentRow, ""); someMore = false; } } return someMore; } } 272 Chapter 14 Optimizing for Application Speed Download from www.wowebook.com ptg Of course, we must change getAndDisplayCities(...) so it will use our new DeferredCommand class; the difference is in the last executable sentence shown here. void getAndDisplayCities() { if (currentStart < 0) { currentStart = 0; } displayEmptyCities(0, "Loading..."); getEnvironment().getModel().getCities( getDisplay().getCountryState().getCountry(), getDisplay().getCountryState().getState(), currentStart, CitiesBrowserView.CITIES_PAGE_SIZE, new SimpleCallback>() { @Override public void goBack( final LinkedHashMap result) { DeferredCommand .addCommand(new DeferredCitiesDisplay(result)); } }); } } In terms of the user experience, this solution may feel much like the Timer-based one, but display will actually be faster and smoother. If the user isn’t interacting with the browser, successive runs will be scheduled as soon as possible, taking the most advantage of the available CPU power. Bundling Data Since GWT 1.4, you could use “image bundles” to improve application load perform- ance, by making fewer calls to the server to get whichever images were required for your site. We already mentioned earlier in this chapter that your client page won’t be able to do any number of simultaneous calls to the server, and this implies that if your applica- tion requires many images or icons, there will be a delay while the browser queues all calls to the server. (You could use Speed Tracer or any other of the tools that we will study later in this chapter to see the sequence of short load times in action.) This is not the only problem. Because the kind of images you will use will surely be small (you wouldn’t want to be downloading large files in any case, would you?) then HTTP will add a not trivial-sized overhead; it could even surpass the size of a given icon! And, even if your pictures won’t be changing (just how often do you redesign your icons?) the browser will have to do a request to check whether the cached image may be considered still fresh. The ImageBundle interface provided a way to solve all these problems by building a single package out of many files, and providing access to it through a Java object, in a 273Design Patterns for Speed Download from www.wowebook.com ptg way similar to the use of the Constants i18n interface that we saw in Chapter 12, “Internationalization and Localization.”9 This interface has now been deprecated, and replaced by ClientBundle, which allows including other types of files, not necessarily images.10 A ClientBundle interface can give access to: n DataResource elements, which provide an URL that enables getting the file con- tents at runtime. n TextResource elements provide access to the contents of a text file, which are included in the compiled file. n ExternalTextResource elements are similar to TextResource ones, but the text is obtained at runtime via Ajax. n CssResource lets you inject CSS files into your application.11 n Finally, ImageResource is the familiar type for ImageBundle users; it provides access to an image. Let’s work out a simple example, using several of the types mentioned. I downloaded several of the images used in Google’s GWT web site and also created a PDF out of the ClientBundle documentation page.12 I listed a directory’s contents into text files (in long and short formats) to provide more variety. The first part is creating an interface, as when working with Constants. package com.kereki.clientbundles.client; // ...imports... public interface SampleResource extends ClientBundle { We can take advantage of a static variable to use this interface as a Singleton. public static final SampleResource RESOURCE = GWT .create(SampleResource.class); Now, as in i18n work (see Chapter 12) you must define a method for each file you want to access. The @Source(...) annotation is used to specify the file’s name; by default, they are to be placed in the client directory. Note that i18n applies here; if you specify a file named something.txt, and your locale is es_UY, GWT will first look for something_es_UY.txt, then for something_es.txt, and finally for something.txt. 274 Chapter 14 Optimizing for Application Speed 9. Web programmers know this technique as CSS sprites and that was what GWT applied internally. 10. As with constants, you also have a ClientBundleWithLookup interface, which allows getting resources by name. 11. This is actually an understatement; see http://code.google.com/webtoolkit/doc/latest/ DevGuideClientBundle.html#CssResource for other extra functionalities. 12. See http://code.google.com/webtoolkit/doc/latest/DevGuideClientBundle.html for the original. Download from www.wowebook.com ptg @Source("gwt_large_logo.png") public ImageResource gwtLargeLogo(); @Source("gwt_small_logo.png") public ImageResource gwtSmallLogo(); @Source("download_gwt.png") public ImageResource gwtDownload(); @Source("learn_more.gif") public ImageResource gwtLearnMore(); @Source("read_the_docs.gif") public ImageResource gwtReadTheDocs(); @Source("ClientBundleDoc.pdf") public DataResource clientBundleDocumentation(); @Source("detailed_list.txt") public TextResource longListing(); @Source("short_list.txt") public ExternalTextResource shortListing(); } Using this bundle is easy. Let’s just create a single page that will show all icons, the PDF file, and both listings. The final page will look as shown in Figure 14.2. The code for this application is quite simple; I didn’t worry much about elegance! First, we create some panels: a vertical one for general layout and a horizontal one for the icons. package com.kereki.clientbundles.client; // ...imports... public class Clientbundles implements EntryPoint { @Override public void onModuleLoad() { final VerticalPanel vp = new VerticalPanel(); final HorizontalPanel hp1 = new HorizontalPanel(); hp1.add(new Image(SampleResource.RESOURCE.gwtLargeLogo())); hp1.add(new Image(SampleResource.RESOURCE.gwtSmallLogo())); hp1.add(new Image(SampleResource.RESOURCE.gwtDownload())); hp1.add(new Image(SampleResource.RESOURCE.gwtReadTheDocs())); hp1.add(new Image(SampleResource.RESOURCE.gwtLearnMore())); vp.add(hp1); 275Design Patterns for Speed Download from www.wowebook.com ptg Figure 14.2 A sampler application, showing the usage of resource bundles to display some images, a PDF file, and text files by two different means. Using a DataResource is simple; we will create a Frame and load it with the PDF file we created. final Frame showPdf = new Frame(SampleResource.RESOURCE .clientBundleDocumentation().getUrl()); showPdf.setSize("540px", "200px"); vp.add(showPdf); For a TextResource, it’s even easier, for you can directly access its text with the getText(...) method. final TextArea showListing1 = new TextArea(); showListing1.setText(SampleResource.RESOURCE.longListing() .getText()); showListing1.setSize("540px", "100px"); vp.add(showListing1); 276 Chapter 14 Optimizing for Application Speed Download from www.wowebook.com ptg Things become a bit more complicated with ExternalTextResource types, for you must do an Ajax call to get the file’s contents, and that also allows for errors and excep- tions. We initialize an area with a Loading... text, and after showing it, we get the file text with a getText(...) call, which requires a ResourceCallback object. In its onSuccess(...) method, we’ll actually load the text into the screen widget; otherwise, we’ll report a failure. final TextArea showListing2 = new TextArea(); showListing2.setText("Loading..."); showListing2.setSize("540px", "100px"); vp.add(showListing2); RootPanel.get().add(vp); try { SampleResource.RESOURCE.shortListing().getText( new ResourceCallback() { public void onError(final ResourceException e) { } public void onSuccess(final TextResource r) { showListing2.setText(r.getText()); } }); } catch (final ResourceException e) { showListing2.setText("Failure!"); } } } As a final step, you will have to add the line to your gwt.xml file. Using bundles is obviously a judgment call; you will exchange speed for size because your generated code will be larger. (The converse of this is code splitting, which we will analyze in Chapter 15, “Deploying Your Application.”) However, if you do require many (hopefully small) files, you will discover that the speed advantages during normal execu- tion (and the faster future visits, due to the cached data) justify the somewhat larger ini- tial download time. Speed Measurement Tools The preceding patterns we saw are to be used at source code level, knowing that they will produce speed improvements. Let’s now examine tools that will let you analyze the actual running code, to determine where your application is actually spending its time, and which are the causes of your possible slowdowns. 277Speed Measurement Tools Download from www.wowebook.com ptg We will consider several browser general (meaning they could also be used for non- GWT applications) measurement tools. Personally, I find no “top” tool, so I regularly use all of them. It’s likely they will converge over time, but for the time being they don’t exactly offer the same functionality or suggestions.13 Speed Tracer Speed Tracer is the first of our “browser general measurement tools” we spoke about ear- lier. It is provided as an extension for the Google Chrome that enables you to find per- formance problems in any web application by enabling you to visualize and analyze low level metrics.14 This tool will show you a graphic picture indicating clearly where your application spends most time and will also pinpoint specific problems and provide suggestions for fixing them. Installing Speed Tracer isn’t hard15 but you must remember to add the --enable- extension-timeline-api parameter when you open Google Chrome. Using it is sim- ple; just navigate to the page you want to analyze, and click the green stopwatch button to start capturing events and times; the red button will stop the data capture. I analyzed an actual (i.e., in production) small GWT application. See Figure 14.3. The graphic at the top shows you the “peaks” of processing, with the corresponding timeline. You can focus on a specific period by dragging the selection bars, or by clicking and dragging. This should be the first information you study, because it can help focus on the “hot spots”; the tall, wide areas, and in particular, the short vertical marks that pinpoint specific problems. (Height stands for activity, so the rule to apply is simple: High is bad, low is good.) You can hover the mouse over a spot to get details on the par- ticular event. If you are interested in only certain types of events, click the magnifying glass, and a filter bar will appear and let you specify your selection criteria. If you select the Network view, you can see what resources (downloaded images, called services, and so on) were actually used. (See Figure 14.4.) You get a separate time- line for each resource. Also, if there is a hint or suggestion, a severity-color-coded icon (red=serious problem; orange=warning; green=hint) appears next to each resource, matching vertical marks in the timeline. By clicking a line you can get even more detailed information. 278 Chapter 14 Optimizing for Application Speed 13. If you are willing to modify your project’s source code to get runtime statistics, you might want to consider the “Lightweight Metrics System” at http://code.google.com/webtoolkit/doc/latest/ DevGuideLightweightMetrics.html used with gwt-debug-panel at http://code.google.com/p/gwt- debug-panel/. Setting it up requires several steps, and then you also have to add appropriate calls at the places you want to measure, but on the positive side, it will provide you with information you couldn’t get otherwise. 14. See http://code.google.com/webtoolkit/speedtracer/. 15. See http://code.google.com/webtoolkit/speedtracer/get-started.html#downloading. Download from www.wowebook.com ptg Figure 14.3 Speed Tracer’s “Sluggishness” report shows you where time was spent and what was being done. 279Speed Measurement Tools Figure 14.4 In Network mode, you can see what resources were used and their timeline. Finally, Speed Tracer also offers a Hints mode, in which it highlights your page’s “speed bumps” and possibly suggests appropriate measures. See Figure 14.5. Speed Tracer is the most recent newcomer, but the information it provides is quite to the point and can help getting extra speed. Download from www.wowebook.com ptg Figure 14.5 The Hints document shows your application speed bumps and suggests some ways around them. YSlow YSlow (as in “Why Slow?”) is a plugin for Firefox’s Firebug, developed by the people at Yahoo!16 It’s geared toward suggesting ways of improving the performance of your application by applying sets of rules for well-optimized pages. After starting YSlow (which is itself included in Firebug) you can pick which set of rules to apply (currently there are three sets—small sites and blogs, Classic V1, and YSlow V2—involving 22 rules) and by clicking them you can see which rules it would apply.17 You can even create your own set; for example, if you aren’t planning to use a Content Delivery Network (CDN) you can simply create a new set (possibly based in an existing one) but skipping that rule; that way, you will avoid being nagged all the time with the “get a CDN” suggestion. I applied YSlow to the same application as with Speed Tracer (see Figure 14.6) with the “small site and blogs” set, and it graded each rule in typical school fashion, from “A” (excellent) to “F” (fail). Clicking each rule produces an explanation of the problem, a brief suggestion of the steps you should take to solve the problem, and a Read More link to a fuller description of the rule, what it implies, and how to fix the particular problem. 280 Chapter 14 Optimizing for Application Speed 16. See http://developer.yahoo.com/yslow/ for more on this tool. 17. See http://developer.yahoo.com/performance/rules.html for the latest version of the rules that YSlow applies. Download from www.wowebook.com ptg Figure 14.6 YSlow grades your application on up to 22 rules and suggests measures you should take to optimize it. Next to the Grade tab, you get the Components tab (see Figure 14.7) that shows all the types of resources your application uses (HTML, CSS, and JavaScript files, plus images and icons, and more) with detailed information as to their size, whether they were compressed, the URL they came from, their Expires date, and so on. You can click the magnifying glass icon to get even more detailed information on the specific resource. 281Speed Measurement Tools Figure 14.7 The Components tab shows you all the server resources that were required by your application. Download from www.wowebook.com ptg You can get a resume of the information in the Components tab, by selecting the Statistics tab. This produces a display (see Figure 14.8) with two pie charts showing what would be loaded with an empty cache browser, or what would be loaded if the cache had been already primed. If you are using expiration parameters correctly, the second chart would show that much less data has to be downloaded. 282 Chapter 14 Optimizing for Application Speed Figure 14.8 The Statistics tab shows you how much data would be downloaded with an empty or primed cache. Finally, the Tools tab provides many options, some of which are of interest. n JSLint runs this analyzer18 on all your JavaScript code. Note that it won’t be useful for most of your code—the GWT generated part—and you will probably be able to use it only for external libraries. As the JSLint creators point out, “JSLint will hurt your feelings” but its suggestions are worth it. n All JS Minified lets you see the JavaScript code, as it would look if minified. GWT takes care of doing it for its own code, so once again you will only use this for external libraries. n All Smush.it runs this Yahoo! tool19 that can apply a lossless transformation to reduce the size of your image files without affecting their quality, letting you download equivalent optimized versions of them. n Printable View presents all the information in the Grade, Components, and Statistics tabs in a single printable page. YSlow thus offers not only a good analysis of your page, but also suggests ways of fix- ing whatever problems it found, even providing sometimes (as with JavaScript minifica- tion and image optimization) the full solutions you require. 18. See www.jslint.com/ for more on JSLint. 19. See http://developer.yahoo.com/yslow/smushit/ for information on how Smush.it works. Download from www.wowebook.com ptg Page Speed Google’s own Page Speed20 is another Firebug plugin, in some aspects quite similar to YSlow, but worthy enough of your attention; in fact, I usually apply both, to make sure I’m not missing anything. For Page Speed to analyze a page, you’ll have to start FireBug, pick Page Speed, and click Analyze Performance. The main result it will provide is a page score, from 0 to 100, which reflects the quality (in terms of the predefined rules) of your application. See Figure 14.9. 283Speed Measurement Tools 20. See http://code.google.com/speed/page-speed/ for the Page Speed site. 21. See http://code.google.com/speed/page-speed/docs/rules_intro.html for the list of applied rules. Figure 14.9 Page Speed grades your application from 0 to 100 and points out its problematic areas in terms of unsatisfied web design rules. Each rule includes an icon (red for serious problems, yellow for warnings, green for approved parts, and blue for information) and by clicking on the plus sign next to the icon, you can get a more detailed explanation of the problem and a suggestion as to the required fix.21 See Figure 14.10. Download from www.wowebook.com ptg Figure 14.10 Clicking a rule provides further explanation as to the problem and a suggestion or fix. The Page Speed menu bar provides a Performance tab (whose results we have already seen) and a Resources tab. The latter shows you all the resources (images, files, and more) that were downloaded from the server, with further information as to URL, size, and so on. (See Figure 14.11.) You should study this page, and check whether most of the fixed resources could be loaded from the cache, and if files are being compressed; not satisfying these conditions would negatively impact the performance of your application. Finally, a Page Speed Activity extra tool is provided (see Figure 14.12) that produces a detailed timeline showing all the requests to the server, color coded by type, and with bars proportional to their actual times. Having many of these bars roughly at the same time would indicate the need for joining files together (possibly by using bundles) or for caching, whereas long bars could possibly point out server delay problems or too long processes. As we said, Page Speed is quite similar to YSlow (and even to Speed Tracer, though that runs on a different browser) but being able to apply different sets of rules and taking different measurements is the equivalent of going to several different doctors to confirm a diagnosis, so I’d insist on using all tools, even if at times there is some considerable overlap between them. 284 Chapter 14 Optimizing for Application Speed Download from www.wowebook.com ptg Figure 14.11 The Resources tab provides information on every request to the server. 285Speed Measurement Tools Figure 14.12 The Page Speed Activity lets you analyze a timeline for your application, detecting bottlenecks and too many concurrent calls. JavaScript Debuggers Let’s finish the analysis of browser-based tools with JavaScript’s own debuggers. Firefox’s Firebug, which we have already seen, has a Net panel that can produce a detailed list of all events and resources invoked by your application (see Figure 14.13). This isn’t as good as Speed Tracer’s analysis, for example, but it can do as a starting point. In a similar vein, you could use Safari’s debugger, or Opera’s DragonFly debugger (see Figure 14.14), which can also produce a detailed timeline of events. Of course, because you cannot at the time use Opera for GWT Development mode, it’s less likely that you would want to use this browser. Download from www.wowebook.com ptg Figure 14.13 Firefox’s Firebug debugger includes a Net tab, which shows all events and their durations. 286 Chapter 14 Optimizing for Application Speed Figure 14.14 Opera’s DragonFly debugger can also help; the only problem is that you cannot use Opera for GWT development. Debuggers aren’t specifically geared toward optimization, but on the other hand, they are always available as you do your development, so why miss using them? Summary We have seen three ways of enhancing the performance of your application: design pat- terns, some GWT 2 new features, and browser speed measurement tools. By combining all these solutions, you can find bottlenecks, detect slowdowns, and generally squeeze much more performance out of your application. There’s no single “silver bullet” that can fix all possible problems, but applying what we have seen here, you are on your way to a far more responsive application. Download from www.wowebook.com ptg 15 Deploying Your Application This chapter explores the final steps in your development process: how to compile and deploy your application, how to create your own shareable modules, and how to reduce the load time for your application by splitting the code. In the past chapters, we have been dealing with ways to write efficient, streamlined, modern Internet applications, but it happens there still are some ways to squeeze out yet a bit more of speed and get a faster page load, so we need to look into that. And, obvi- ously, if you cannot deploy your application, all your work will have been for naught; let’s also see how to “finish the job” and set up your page for production. Compilation With standard GWT development techniques, you won’t have to compile your program until you actually want to publish it. Because of how deferred binding works, several dif- ferent versions of the final JavaScript code will be produced: the number of supported browsers (currently, six, but the number may change from GWT version to version) times the number of supported locales (in our case, four: see Chapter 12, “Internationalization and Localization”). See Figure 15.1. You can use many compiler options to speed up compilation, to enhance the quality of the produced code, or just to inspect what kind of code is generated; let’s turn to this now. Plenty of compiler options aren’t that well documented, so let’s give a glance at least to the most important ones, meaning those you are likely to use. n -compileReport creates the Story of Your Compile report; we’ll be using this for code splitting. This option used to read -soyc but the name was quickly changed. n -draftCompile enables a faster, but with fewer optimizations, compilation process. You should use this option while developing, but leave it out when pro- ducing the definitive, production code. n -ea enables assert checks; otherwise, assert statements would be ignored. n -extra aDirectory allows you to specify to which directory should extra files (not meant to be deployed) be written. Download from www.wowebook.com ptg Figure 15.1 The GWT compile process produces a distinct permutation for each combination of browser type and locale. The numbers match those shown in Figure 15.2 but might change from compile to compile. n -localWorkers someNumber lets you specify how many local workers (i.e., com- piling processes) to run at the same time; by default, it’s just one. You should exper- iment a bit with this; depending on the number of CPUs in your machine, you could get faster compiles by setting it to a higher value. n -logLevel lets you set the level of messages (ALL, DEBUG, ERROR, INFO, SPAM, TRACE, or WARN) you will get when compiling. n -module someModules lets you specify which module(s) to compile. n -out aDirectory lets you specify to which directory to write output files. n -style outputStyle lets you set the output JavaScript style to DETAILED, OBF (obfuscated, the default value), or PRETTY. n -treeLogger produces log output in a graphical tree form. n -validateOnly performs a complete validation of all source code, without actu- ally compiling anything. You could use it as a fast check before a long compile. 288 Chapter 15 Deploying Your Application en_GB en es 0 6 12 18 1 7 13 19 2 8 14 20 3 9 15 21 4 10 16 22 5 11 17 23 IE8 Default Safari Gecko1_8 Gecko Opera IE6 Download from www.wowebook.com ptg n -war aDirectory lets you set to which directory should deployable output files be written; by default, it is “war”. n -workDir aDirectory allows you to specify which directory should be used by the compiler for internal use; by default, it is the system’s temporary directory, and in any case, it should be writeable. n -XdisableAggressiveOptimization disables aggressive optimizations. n -XdisableCastChecking foregoes checking if a cast operation—as in (someCast)anObject.someMethod(...)—can throw a ClassCastException, thus speeding method calls. n -XdisableClassMetadata disables the usage of the getName(...) method but allows reducing the final code size because GWT doesn’t have to include any class information within the produced JavaScript code. n -XdisableRunAsync disables code splitting. (We’ll be studying this in more detail.) Adding compiler options with Eclipse just requires clicking the red Google Compile box, and then clicking Advanced, and entering the desired options in the Additional Compiler Arguments box. If you want to compile your code in the fastest way, you should experiment by using -draftCompile -localWorkers someNumber -XdisableAggressiveOptimization and also add in your application’s gwt.xml file lines such as the following, so only one code version will be produced.1 On the other hand, if you care for the final produced code performance, you should rather consider including -XdisableCastChecking -XdisableClassMetadata -style OBF so that your code will be as compact as possible. Modules While developing an application, it’s a given that you will develop classes (tools, widgets, whatever) that you will want to reuse in other applications. GWT lets you package your classes as modules for future reuse, but there are some particularities that you need to be aware of. 289Modules 1. The default suggestion for implementing this was creating a new module that inherited your original module (i.e., the one defined in the gwt.xml file), adding the elements to it, changing your host HTML file so it would refer to this new module, and compiling it instead of the original one... but I do think the method shown in the text is easier. We saw this briefly in Chapter 3, but see “Renaming Modules” at http://code.google.com/webtoolkit/doc/ latest/DevGuideOrganizingProjects.html#DevGuideModuleXml for more on it. Download from www.wowebook.com ptg The main problem here is that you won’t be able to produce a jar file and simply reuse it as you were used to with common Java development, because GWT requires the actual source code of your class to compile it into JavaScript. (Of course, for server pack- ages, this doesn’t apply, and you can use your standard, run-of-the-mill jar files without further ado.) To test this out, let’s create a separate module for the KeyValueMap class that we wrote in Chapter 4, “Working with Browsers”. 1. First, create a new empty project: KeyValueMap is a good name, and the package can be com.kereki.keyvaluemap. Edit its gwt.xml file to remove the element. (Actually, you could have an entry point, if your class imple- mented the EntryPoint interface. When you load a project including two or more entry points, the code from all the onModuleLoad(...) methods gets executed before anything else.) 2. Move the KeyValueMap.java file from the original project to the client directory in our new project. (Red error marks should pop up all over your original project, showing that the KeyValueMap class is now missing.) 3. Compile the new project, so it will generate a class file in the war output direc- tory. We will require this file for our module, together with its source file. 4. Create a temporary directory somewhere in your machine, and copy both the source and class files to it: md /tmp/newmodule cd /tmp/newmodule cp -R /home/fkereki/workspace/KeyValueMap/src/com/ . cp -R /home/fkereki/workspace/KeyValueMap/war/WEB-INF/classes/com/ . 5. Create a jar file for your module by doing jar -cvf KeyValuemap.jar . added manifest adding: com/(in = 0) (out= 0)(stored 0%) adding: com/kereki/(in = 0) (out= 0)(stored 0%) adding: com/kereki/keyvaluemap/(in = 0) (out= 0)(stored 0%) adding: com/kereki/keyvaluemap/client/(in = 0) (out= 0)(stored 0%) adding: com/kereki/keyvaluemap/client/KeyValueMap.java(in = 1627) (out= 714)(deflated 56%) adding: com/kereki/keyvaluemap/client/KeyValueMap.class(in = 2108) (out= 1145)(deflated 45%) adding: com/kereki/keyvaluemap/shared/(in = 0) (out= 0)(stored 0%) adding: com/kereki/keyvaluemap/server/(in = 0) (out= 0)(stored 0%) adding: com/kereki/keyvaluemap/KeyValueMap.gwt.xml(in = 947) (out= 398)(deflated 57%) 6. Add a modules directory to your old project at the same level as src, and copy the newly created jar file to it. You would use this directory for all modules you add to a project. 290 Chapter 15 Deploying Your Application Download from www.wowebook.com ptg 7. Add an line to the gwt.xml file in your old project. 8. Add the new jar to the classpath of your old project. (The red error marks should now disappear.) You are done! If you study step 7, you will notice that this is exactly the same way GWT requires that you include com.google.gwt.user.User and other modules for your application! Code Splitting If your application grows (and that’s a tendency hard breaking off from!) the initial download will become large enough to become too noticeable, and the user will not appreciate it. Since version 2, GWT provides Dead For Now (DFN) code splitting, which lets you download first only what you need, and then get the rest on demand, if and when it is needed. Of course, if you require the same code a second time, no further downloads will be required, because the code will already be in memory; there will be a trade-off between a shorter initial download time and small future extra downloads, but the cost will be paid only once per code split.2 To split your code, you’ll just use the GWT.runAsync(...) method. This will call the server, download the required code, and then onSuccess(...) execute it.3 The stan- dard pattern will then be GWT.runAsync(new RunAsyncCallback() { @Override public void onFailure(Throwable caught) { // ...warn about the download failure... } @Override public void onSuccess() { // ...this is where the original code goes... } }); You may have scope problems, because your original code will now be running in the RunAsyncCallback object scope, but they are usually simple to solve. Let’s try some actual experiments with the Environment menu handling code; given that it’s highly likely that not all menu functions will be used (at least, in the same session) by a user, it 291Code Splitting 2. You can read more of the official word on Code Splitting at http://code.google.com/webtoolkit/doc/ latest/DevGuideCodeSplitting.html. 3. A not minor point: You should plan for failure—onFailure(...)—because the code download might fail. There is, frankly, little than you can do, but at least you should explain to the user why he isn’t getting the form he expected. Download from www.wowebook.com ptg stands to reason that these code splits will help.4 A part of the menu code (see Chapter 4) used to read: ...} else if (token.equals(CitiesBrowserPresenter.PLACE)) { panel.add(new CitiesBrowserPresenter(args, new CitiesBrowserView(), this).getDisplay().asWidget()); } ... We could split off the Cities Browsing code, by rewriting the else as ...} else if (token.equals(CitiesBrowserPresenter.PLACE)) { final Panel myPanel = panel; final String myArgs = args; GWT.runAsync(new RunAsyncCallback() { @Override public void onFailure(final Throwable reason) { Environment.this .showAlert("Couldn't run the Cities Browser code!"); } public void onSuccess() { myPanel.add(new CitiesBrowserPresenter(myArgs, new CitiesBrowserView(), Environment.this).getDisplay() .asWidget()); } }); }... We had to add some final attributes to get at the panel and args variables, but other than that, the transition is simple; we also had to change the this reference to Environment.this because of closure problems. We could even do some refactoring to simplify splitting off more parts of the code. First, we could extend the RunAsyncCallback(...) interface by writing:5 abstract class MyRunAsyncCallback implements RunAsyncCallback { Let’s have a few attributes to store the arguments that are required for the code. We’ll pass the said arguments to our new constructor. 292 Chapter 15 Deploying Your Application 4. And, if you feel it will be quite likely that a certain piece of code will get used, but didn’t want to load it right at the beginning because of download time reasons, you could apply a variation of the prefetching pattern we used earlier in the book: Do a GWT.runAsync(...) call with an empty onSuccess(...) method, and thus use background time to get the code loaded in advance of its being required. 5. Yes, and I admit the MyRunAsyncCallback name is kind of lame... Download from www.wowebook.com ptg String myOwnArgs; Panel myOwnPanel; Environment myOwnEnvironment; String myOwnErrorMessage; public MyRunAsyncCallback( final String args, final Panel panel, final Environment environment, final String errorMessage) { myOwnArgs = args; myOwnPanel = panel; myOwnEnvironment = environment; myOwnErrorMessage = errorMessage; } The onFailure(...) method is now trivial. @Override public void onFailure(final Throwable reason) { myOwnEnvironment.showAlert(myOwnErrorMessage); } } Using this code requires writing the onSuccess(...) method. For example, we might split off the Cities Creator form, by means of ...} else if (token.equals(CityCreatorPresenter.PLACE)) { GWT.runAsync(new MyRunAsyncCallback(args, panel, this, "Couldn't load the cities browser code") { @Override public void onSuccess() { myOwnPanel.add(new CityCreatorPresenter(myOwnArgs, new CityCreatorView(), myOwnEnvironment) .getDisplay().asWidget()); } }); }... If you compile both versions of the code (with and without the code split) you will notice that some files get smaller, but several more files are produced. In these examples, because the Cities Browsing and Creation classes aren’t used elsewhere, all their code will be removed from the initial download, reducing its size. But how can you know for sure? Let’s analyze an important tool, the Compile Reports, which for a short while were known as Story Of Your Compile, or SOYC—and this will also help understand the compile process. 293Code Splitting Download from www.wowebook.com ptg Turning on the Compile Reports option will produce a directory with a set of HTML files, which comprise the required report. These reports will give you a graphical representation of the results of the compile process and provide you information to find possible code reduction hints to analyze code splitting problems and to let you work out further code optimizations.6 The Compile Report is a group of static HTML pages and can be found at the extras directory (in my case, at my home directory, at workspace/mvpproject/extras) in the mvpproject/soycReport directory; you can examine it by opening the index.html file that is situated there. (See Figure 15.2.) In our case, because we were creating code 294 Chapter 15 Deploying Your Application 6. Official usage notes on Compile Reports can be found at http://code.google.com/webtoolkit/ doc/latest/DevGuideCompileReport.html. Figure 15.2 The basic compile report shows all permutations that were generated by the compilation process. The permutation numbers match those shown in Figure 15.1 but might vary. Download from www.wowebook.com ptg for four locales (the default one, plus the three ones we defined in Chapter 12), and GWT always produces code for six browser types, we should get 24 permutations, num- bered 0 to 23. If we click on a specific permutation, we can see its size details. (See Figure 15.3.) 295Code Splitting Figure 15.3 Each permutation report shows the full code size (without any splits), the initial download size, and more data on each split. The Full code size value represents the total size of the code: 224,402 bytes in this case. You can see that the initial download size, given the two code splits we made, would be 203,161 bytes, representing about a 9% reduction in size; not bad for such a small change! The rest of the code is comprised of two code splits (at 11,323 and 4,578 bytes), plus a “left over” split (5,340 bytes) with some general code, not associated specifically with any split; you can check that the sum works out. By clicking on a “report” link, you can get (see Figures 15.4 and 15.5) further reports showing in more detail how the code size is achieved. By clicking on the package links, you can eventually get to see why a specific class is included; this is a good help in case something unexpected happens, and you don’t get the size savings you hoped for. If sizes do not match your expectation (you tried to create a separate fragment for a specific part of the code, but GWT insists on downloading it from the beginning) exam- ining the dependencies will let you find why you failed in separating it. (The most com- mon reason is that, somehow, you use a class that you wanted to split, from an unsplit part of the code.) You’ll have to reorder or reorganize part of your code so this won’t happen, and there isn’t any specific technique for this, but hopefully you’ll work it out. Download from www.wowebook.com ptg Figure 15.4 Clicking on a Report link shows in higher detail how the code size is divided. 296 Chapter 15 Deploying Your Application Figure 15.5 An analysis of a specific code split. In this case, all the present code comes from a single class, which we wanted to split, so we got what we wanted. You should get used to studying the Compile Report; even if your code is in tip-top shape, some new programmer might introduce a change that disrupts your splits. Before distributing an updated version of your application, check that the code downloads are still what they used to be; a perfectly valid statement might go so far as to pull up all of Download from www.wowebook.com ptg your splits into “initial download country,” and while the program would still work the same, the user would feel a throwback to worse performance. Deployment After all we did earlier in this chapter, deploying your application is trivially easy. The compiler produces code files following the WAR rules, which makes it simple to deploy. Whether you are working with GWT only for client-side coding, or you are going the “Java way” with server-side code, getting your application on the web will require copy- ing only a few directories and files.7 Let’s start with the simpler case first.8 Working with Client-Only GWT If you don’t have Java-based server-side coding (as with a web service-based architecture) you can easily deploy the HTML and JavaScript files that are produced by the GWT compiler to a web server such as Apache, though the details would be similar for other programs. By default, in Linux you can usually find Apache’s pages at /srv/www/htdocs (for OpenSUSE) or /var/www/html, and you can actually set it to store its pages at any other place; check the configuration files. If you do not have any remote servlets, you’ll just have to copy the files in the output war directory to the correct location for your home page, and you’ll be set. Working with Client-Plus-Server GWT If you are going to have servlets (i.e, Java server-side coding) you will need an appropri- ate web container. GWT produces a standard deployment configuration, so even though in this section we will be working with Tomcat (version 6.0.20) as a web container, changing to other container wouldn’t be much of a problem.9 We won’t be covering how to set up that part of the software stack, but there’s plenty of documentation every- where for that. Of course, if your application depends on web services, Enterprise Java Beans, or any other such technology, you’ll also have to set them up. Working with OpenSUSE, Tomcat stores the web pages at /srv/tomcat6/webapps; other Linux distributions, and of course Windows and Mac versions, may store them at different locations. All we have to do is copy (and surely rename; I chose mvpproject) the war directory in our project to a directory in the Tomcat directory. To run the 297Deployment 7. You could also work with the generated Ant scripts to build or deploy your application; in my case, I prefer working exclusively within Eclipse. 8. While I’m not an Ant user, it should be said that you can automate both compilation and deploy- ment with it, and there are many GWT developers who swear by it! 9. See http://tomcat.apache.org/ for more on Tomcat. Download from www.wowebook.com ptg application, you’ll have to navigate to http://yourOwnServer:8080/mvpproject/ Mvpproject.html and you will get something like Figure 15.6.10 298 Chapter 15 Deploying Your Application 10. A slight detail: with the current version of the Eclipse plugin, the war directory is both used as input and output, but this is expected to be fixed soon. Figure 15.6 Running our Tomcat-deployed application on Linux-based Google Chrome shows our deployment was successful. Of course you could configure Tomcat to find the host HTML page anywhere on your web server, but mind that all resources should be placed so as to mirror the project paths, because references to them are relative. If you are using servlets, GWT will automatically deploy them to the WEB-INF/ classes directory. In our example, you could do # cd /srv/tomcat6/webapps/mvpproject/ # cd WEB-INF/classes/com/fkereki/mvpproject/server # ls -ld * -rw-r--r-- 1 root root 3847 2010-03-12 05:16 FileProcess.class -rw-r--r-- 1 root root 1611 2010-03-12 05:16 FileProduce.class -rw-r--r-- 1 root root 3458 2010-03-12 05:16 LoginServiceImpl.class -rw-r--r-- 1 root root 3182 2010-03-12 05:16 Security.class -rw-r--r-- 1 root root 1642 2010-03-12 05:16 ServerCityData.class -rw-r--r-- 1 root root 7620 2010-03-12 05:16 WorldServiceImpl.class -rw-r--r-- 1 root root 2986 2010-03-12 05:16 XhrProxyImpl.class and check that all remote servlet code is present and up to date. If you required any other server-side classes, you would also place them in this directory. The web.xml file (which we created with the GWT project; see Chapter 2, “Getting Started with GWT 2”) Download from www.wowebook.com ptg will have to provide all necessary definitions and mappings. Its final version for our application could be as follows.11 loginServlet com.fkereki.mvpproject.server.LoginServiceImpl loginServlet /mvpproject/login worldServlet com.fkereki.mvpproject.server.WorldServiceImpl worldServlet /mvpproject/world xhrProxyServlet com.fkereki.mvpproject.server.XhrProxyImpl xhrProxyServlet /mvpproject/xhrproxy fileProcess com.fkereki.mvpproject.server.FileProcess 299Deployment 11. Note that with previous versions of GWT, you had to do this in the application gwt.xml file. Download from www.wowebook.com ptg fileProcess /mvpproject/fileprocess fileProduce com.fkereki.mvpproject.server.FileProduce fileProduce /mvpproject/fileproduce Mvpproject.html Finally, note that if your application uses RPC, GWT will take care of copying the gwt-servlet.jar file to the WEB-INF/lib directory, but if you require any other jars, you’ll have to copy them by yourself. Summary In this chapter we have finished the complete application development cycle, by actually compiling and deploying our code. We have also seen a method for optimizing the appli- cation download code, by means of splitting it into significant parts, and also for analyz- ing and fixing any situations that might lead to worse-than-expected reductions in size. By combining this method with the previously seen techniques, you will make true the promise of web applications that feel so responsive as if they were actually deployed and installed on the user’s PC, rather than downloaded from the Internet and executed on a client-server basis. 300 Chapter 15 Deploying Your Application Download from www.wowebook.com ptg Index Numbers and Symbols 007. See Bond, James $('a'), as selector, 142 A Acceptance testing, with Selenium example of, 255–256 overview, 253–255 potential problems, 257 "Access Control Specification," 121 Accounting, security and, 178 Accuweather, 158 actualUrl value, 175 addCity(...) function coding server side services and, 90 WorldService remote service and, 85 addValueChangeHandler method, 97 aDirectory, compilation and, 287, 288, 289 Agile Software Methodologies, 7, 10 Ajax caching and, 260 ExternalTextResource types and, 277 receiving/processing XML and, 127–128 security controls and, 179 sending XML via, 136 stateless server-side coding and, 183–184 Download from www.wowebook.com ptg AjaxLoader API, 160–161 ... construct, 45 Albany, a city in NY, USA, 108, 175 Alt+Backspace/Back button problem creating menus, 41–43 displaying forms in pop-ups, 37–38 History class, 33–34 overview, 31–32 passing parameters, 38–41 setting up HTML page, 32 starting application, 34–37 American Museum of Natural History, 168–170, 173–174 andReturn(...) method, 245–246 animateAllLinks(...) function, 143 Ángel S. Adami, 158 Animations, JSNI and, 143 Annotations, 215 Antique browsers, 52–53, 120–121 ... construct, 45 Apache client-only GWT deployment and, 297 Commons Lang component, 183, 200–202 Tomcat servlet container, deployment and, 297–300 appendChild(...) method, 134–135 Application deployment with client-only GWT 2, 297 with client-plus-server GWT 2, 297–300 code splitting, 291–297 compilation, 287–289 getting started, 20 modules, 289–291 overview, 287 summary, 300 Application development GWT advantages, 4–5 overview, 1 Rich Internet Applications and, 1–4 software methodologies for, 5–8 summary, 8 Application Programming Interfaces (APIs), adding dashboard visualizations, 162–168 overview, 157 summary, 175 weather vane, 157–162 working with maps. See Maps Application speed, optimizing design patterns for. See Design patterns, for speed measurement tools for. See Speed measurement tools overview, 259 Application testing acceptance testing with Selenium, 253–257 integration testing with GWTTestCase, 247–253 JUnit and. See JUnit testing overview, 229 reasons for, 229–231 summary, 257 ARCFOUR, 180–183 AreaChart objects, 164, 167 arguments, JSNI and, 140 Assembly language, 139 assertArrayEquals(...) methods, 235 assertFalse(...) methods EasyMock and, 245 GWTTestCase and, 251 JUnit testing and, 236 302 AjaxLoader API Download from www.wowebook.com ptg AsyncCallback function callbacks and, 59 coding server side services and, 89 JSONP and, 154–155 Authentication, security and, 178 Authorization, security and, 178 Automatic testing of GWT. See Application testing Automatically tested code, 229–231 Availability, security and, 178 B Back button/Alt+Backspace problem creating menus, 41–43 displaying forms in pop-ups, 37–38 History class, 33–34 overview, 31–32 passing parameters, 38–41 setting up HTML page, 32 starting application, 34–37 baseUrl method, 136 Beta testing, 7–8 Bond, James, 144–145, 226 Browser recognition classic way, 43–44 deferred binding way, 44–47 disabled JavaScript and, 53 of older IE, 52–53 overview, 43 Browser(s) based measurement tools. See Speed measurement tools Country/State cities, 101–108 differences, 4–5 GWT Developer Plugin to operate, 28–30 HTMLUnit, 247 older, 52–53, 120–121 security, SOP restriction and, 119–121 "Web as Platform" and, 2 XML parser, 125–127 Browsers, working with Back button problem. See Back button/ Alt+Backspace problem code generation, 47–52 detecting user's. See Browser recognition overview, 31 summary, 53 BufferedReader function, 129 Bug prevention, 229–231. See also Security; Security, servers and Bundles, resource annotations and, 215 internationalization and, 212–213 localization and, 224–227 UiBinder-based internationalization and, 219–223 using constants and, 213–214 Bundling data, for application speed, 273–277 Button function, JSON and, 147–150 C Caching application speed and, 260–263 prefetching and, 264–265 Callbacks EasyMock testing and, 245–246 enabling/disabling Login button and, 67–69 GWTTestCase testing and, 252–253 JSON usage and, 149–151 JSONP and, 153–155 Login button, 186–187 MVP implementation and, 59–60 303Callbacks Download from www.wowebook.com ptg Callbacks (continued) Presenter, 98 uploading files and, 197–200 callback=yourownfunction(...) parameter, 153 calledName variable, 242 Calls, from JavaScript, 140–141 Capture<.> class, 244–245 Cascading Style Sheets (CSS), 113–115 Challenges, security AAA for, 178 Ajax problems, 179 full SSL security and, 177–178 overview, 177 changePassword(...) method, 192–193 Changing passwords, security and, 190–193 CheckStyle plugin, 18 Cities Browsing class, 292–293 Cities updating application, 121–125 CITIES_AT_A_TIME constant, 268–269 CitiesBrowserView.ui.xml file, 102–104, 106–108 CITIES_DELAY_IN_MS constant, 268–269 CITIES_PAGE_SIZE constant, 103, 106–107 City browser application sample form, 101 Selenium testing and, 255 thread simulation and, 266–270 element city update application and, 123–124 creating XML and, 132 City input form, 112–113 cityExists(...) function coding server side services and, 91 WorldService remote service and, 85 cityList function, 127 Classes java.lang package, 14 java.sql package, 15 java.util package, 15–16 Classic browser detection, 43–44 Classical methodologies, 5–7 classname, JSNI and, 140 clearAllCities(...) method, 123 clearCities(...) method, 105–106 Client-only GWT, deployment with, 297 Client-plus-server GWT, deployment with, 297–300 ClientBundle interface, 273–277 ClientCityData classes code sharing and, 86–88 coding server side services and, 90, 92 Cloaking, 10 Closure Library, 5 Cloud Computing, 3 Code, automatically tested, 229–231 Code generators, 47–52 Code Inlining compiler and, 13 JSON usage and, 152 Code sharing, 86–88 Code splitting application deployment and, 291–297 compiler and, 12 Code writing, 17–18 codeDecode(...) method, 181–182 Codes, pattern, 225–227 Command objects, 41–43, 47–52 Command pattern, 267, 270 Common operations, security changing password, 190–193 logging in, 185–190 304 Callbacks Download from www.wowebook.com ptg Communicating with other servers city update application, 121–125 overview, 119 producing XML, 131–135 receiving/processing XML, 125–131 sending XML, 135–137 SOP restriction and, 119–121 summary, 137 Communicating with your server introduction to RPC. See Remote Procedure Calls (RPC), introduction RPC patterns of usage. See Remote Procedure Calls (RPC), usage Compilation, deployment and, 287–289 Compile process, in GWT 2, 287–289 Compile Reports tool, 293–296 Compiler, Java-to-JavaScript, 12–14 -compileReport, compilation option, 287 Complex UiBinder examples dealing with constructors, 74–75 presetting properties, 73 using your own widgets, 73–74 working with complex layouts, 75 Components compiler, 12–14 JRE Emulation Library, 14–16 overview, 12 UI library, 17 Components tab, 281–282 Composite widgets Country/State, 101–108 interactive maps and, 171 MVP and, 95 Confidentiality, security and, 178 Constant Folding, 13 Constants interface, 213–214 ConstantsWithLookup interface internationalization and, 213–214 translating error codes and, 215–217 Constructors code sharing and, 86–88 dealing with, 74–75 invoking Java, 141 Controller role, in MVC, 56–57 element city update application and, 123–125 creating XML and, 132–134 Copy Propagation, 13 Country/State cities browser, 101–108 CountryState object, 260–263 CountryStateView widgets Country/State cities browser and, 102–104, 107–108 UiBinder code and, 95–97 createElement(...) method, 134–135 CreateMock(...) methods, 243 createTextNode(...) method, 134–135 Creating XML overview, 131–132 with strings, 132–133 through DOM, 133–135 Cryptography encryption, 180–183 hashing, 180 hashing with JavaScript, 142–143 overview, 179 CssResource elements, 274 Currencies, localization and, 226 currentRow variable, 268–269 CustomFieldSerializer class, 80 D Darwin, Charles, 144, 225, 233–234 Darwin (cities), 122–124, 252–253, 256 305Darwin (cities) Download from www.wowebook.com ptg Dashboard visualizations Google Visualization API, 164–167 handling events, 167–168 overview, 162–164 Data bundling, 273–277 prevalidation, 112–116 Data tables, 165–167 Data transfer object (DTO), 186–189 Database-related widgets, 94–100 DataResource elements, 274–276 Date and Time formats, 224–226 Dates, serialization of, 80 Dead Code Elimination, 12 Dead For Now (DFN) code splitting, 291 "Death of the Desktop" concept, 4 Debuggers, JavaScript, 285–286 Declarative UI basic UiBinder example, 70–73 complex UiBinder examples, 73–75 overview, 69 Default value, 215, 218 defaultLocale attribute, 220 @DefaultStringValue(...) function, 215 Deferred binding replacement technique browser detection with, 44–47 dashboard visualizations and, 164 using constants and, 214 Deferred commands, 270–273 delayTestFinish(...) call, 252–253 Demeter, Law of, 62 Dependency Injection, 56 description attribute, 221–223 deserialize(...) method, 82 Design patterns, for speed bundling data, 273–277 caching, 260–263 overview, 259 prefetching, 263–266 thread simulation. See Thread simulation Desktops, 4 Developing GWT applications. See Application development Development mode, 27–30 DFN (Dead For Now) code splitting, 291 Direct Evaluation RPC (deRPC), 83–84 disableLogin(...) method, 67 Display interface changing password, 191 city update application, 123 file download form, 205 interactive maps, 168–169 uploading files, 197 displayCities(...) method Country/State cities browser and, 105–107 processing XML using Ajax and, 128 producing/sending XML and, 132 displayEmptyCities(...) method, 105–107 displayNews(...) method, 152
interactive maps and, 170 UiBinder and, 220–221 widgets and, 145
function, 144–146 $doc, 142 Document Object Model (DOM) creating XML through, 133–135 GWTTestCase and, 251 doGet(...) method file producing servlet and, 207–208 providing feedback and, 202–204 306 Dashboard visualizations Download from www.wowebook.com ptg Dojo Toolkit, 5, 11 DOM. See Document Object Model (DOM) DomEvent.fireNativeEvent(...) method, 251 doPost(...) method, 201–202 Downloading files file download form, 204–207 file producing servlet, 207–208 overview, 204 -draftCompile, compilation option, 287, 289 DragonFly debugger, in Opera, 285–286 drawZoomAndCenter(...) method, 172–173 DTO (Data transfer object), 186–189 Dummy objects, 240 E -ea, compilation option, 287 EasyMock testing, 19, 240–247 EclEmma plugin, 19, 236–238 Eclipse debugger, and JSNI, 140 JUnit testing and, 234, 236–238 for writing code, 17–18 Einstein, Albert, 234 EjbAccess remote servlet, 116–117 --enable-extension-timeline-api parameter, 278 enableLoginButton(...) method, 67–68 Encryption defined, 179 security and, 180–183 Enterprise Java Beans (EJB), 116–118 element modules and, 290 project structure and, 25–26 Enumerations, serialization of, 80 Environment object changing passwords and, 192 EasyMock testing and, 240–247 MVP implementation and, 60–63, 66–67 Error codes, translating, 215–217 Exceptions GWT 2 and, 13 java.lang package, 14 JavaScript code and Java, 141 java.util package, 15 execute(...) method, 272–273 Extensible Markup Language (XML) city update application and, 123–125 creating, overview, 131–132 creating through DOM, 133–135 creating with strings, 132–133 receiving and processing, 125–131 sending, overview, 131–132, 135–136 sending through Ajax, 136 sending through proxy, 136–137 ExternalTextResource elements, 274–275, 277 -extra, compilation option, 287 Extreme Programming (XP), 229 F fail(...) methods EasyMock testing and, 243 JUnit testing and, 236 Fake objects, 240 Feed, weather, 159–160 Feedback information, 202–204 File processing servlet, 200–202 File producing servlet, 206 307File producing servlet Download from www.wowebook.com ptg Files, moving downloading, 204–208 overview, 195 summary, 209 uploading. See Uploading files FileUpload form, 195–200 final attributes, 80 finishTest(...) call, 252–253 Firebug debugger, 285–286 Page Speed and, 283–285 YSlow and, 280–282 Firefox cross scripting request and, 135 Firebug, 280–286 SOP restriction and, 121 firstResultPosition attribute, 147 Fixed maps, 173–175 Flash library, 164 FlexTable function, 170 Floating point numbers, 13 FormPanel parameters, 196–198 Forms city browser, 101, 255 ClientBundle sampler application, 276 file download, 204–207 file upload, 195–200 passing parameters to, 38–41 in pop-ups, 37–38 UiBinder-based internationalization, 219–223 Full code size value, 295 G Generators, code, 47–52 Generic resource bundles, 212–213 GenericServiceReturnDto class, 187–188 GeoNames, 158 GET calls file download form and, 205–206 processing XML using Ajax and, 127–128 providing feedback and, 203–204 SOP restriction and, 121 getAge(...) method, 152 getAndDisplayCities(...) method, 269, 273 getAttributeNode(...) method, 127 getCities(...) function, 85 getCityName(...) method, 109 getCityPopulation(...) method city update application and, 123 producing/sending XML and, 131–132 getCountries(...) function caching and, 261 database-related widgets and, 97 WorldService remote service and, 85 getCountryState(...) method, 102, 104–107 getDescription(...) method, 141 getDisplay() method, 131–132 getDocumentElement(...) method, 125–127 getElementsByTagName(...) method, 127 getFeed(...) routine, 162 getFormat(...) method, 225 getFromUrl(...) method, 129 getLatitude(...) method, 171 getLongitude(...) method, 171 getModel(...) method EasyMock testing and, 243 MVP implementation and, 60 308 Files, moving Download from www.wowebook.com ptg getModuleName(...) method, 250 getName(...) method, 141 getName(...) method EasyMock testing and, 245–246 file processing servlet and, 202 GetNewsCallback(...) function, 149–150 getNodeValue(...) method, 127 getPassword(...) method, 245–246 getSelections(...) method, 168 getSessionKey(...) method, 187–189 getSize(...) method, 202 getSomething(...) method logging in and, 185–187 MVP implementation and, 62–63, 67 getStates(...) function database-related widgets and, 97 WorldService remote service and, 85 getSummary(...) method, 152 getText(...) call, 277 goBack(...) method, 59–60 Google AJAX Feed API, 159 Google Chart API, 163 Google Chrome Speed Tracer and, 278–280 Tomcat-deployed application, 297–300 Google Gears, 2 Google Maps, 168 Google Plugin for Eclipse coding server side services and, 89 GWT project creation with, 21–22 UiBinder templates and, 70 for writing code, 17–18 Google Testing Blog, 229 Google Visualization API, 163, 164–167 Google Web Toolkit 2. See GWT 2 (Google Web Toolkit 2), getting started google.feeds variable, 162 goto. statements, 257 Grade tab, 281 GreetingServiceImpl class, 81 GWT 2 (Google Web Toolkit 2), getting started advantages/disadvantages, 9–11 components, 12–17 defined, 9 setting up, 17–20 summary, 20 GWT advantages HTML ubiquity/browser differences, 4–5 Java, 10 JavaScript, 5 overview of, 9–11 GWT AjaxLoader API getting weather feed and, 160 steps for using, 160–161 GWT Developer Plugin, 28–30 gwt.ajaxloader.jar files, 160–161 GWT.create(...) creating widgets with, 74 invoking messages with, 217 GWT.getHostpageBaseURL(...) function, 128 GWT.runAsync(...) method, 291–293 gwttest directory, 23 GWTTestCase, integration testing and overview, 247 setup times, 254 testing login view, 247–251 testing servlets, 252–253 gwt.xml files creating modules and, 290–291 Google Visualization API and, 164 GWT AjaxLoader API and, 160–161 GWTTestCase testing and, 252 309gwt.xml files Download from www.wowebook.com ptg H Hashing changing passwords and, 191–193 defined, 179 with JavaScript, 142–143 logging in and, 185–190 security and, 180 hashword.length(...) method, 180 Hints mode, 279–280 History class, 32, 33–34 Host, 119–120 HTML (HyperText Markup Language) setting up page, 32 ubiquity of, 4–5 widgets, 46–47 HTMLPanel function, 147–149 HTMLUnit web browser, 247 Humble Dialog (Humble Object), 56 Hýbl, Cestmír, 144, 146 Hyperlink widgets, 43 HyperText Markup Language. See HTML (HyperText Markup Language) I i18n. See Internationalization (i18n) IE. See Internet Explorer (IE) ImageBundle interface, 273–274 ImageResource elements, 274–275 IncrementalCommand function, 271 element, 25 initialize methods, 81 initializeWithString(...) method, 40 instance objects, 140 instance.@classname::field, 141 Integration testing, with GWTTestCase overview, 247 testing login view, 247–251 testing servlets, 252–253 Interactive maps, 168–173 Internationalization (i18n) annotations tricks, 215 bundling data and, 274 messages and, 217–219 overview, 211–212 resource bundles and, 212–213 summary, 227 translating error codes, 215–217 UiBinder, 219–223 using constants, 213–214 Internet Explorer (IE) recognizing old versions of, 52–53 SOP restriction and, 120–121 IsSerializable interface, 86–88 J jar file, 290 Java advantages of, 10 JavaScript interaction with, 139–141 server-side code, 88–94 UiBinder and, 72–73 Java Cryptography Architecture (JCA), 180 Java-to-JavaScript compiler, 12–14 Java Virtual Machine parameters, 140–141 java.io package, 14 java.lang package, 14–15 JavaScript debuggers, 285–286 deficiencies of, 5 disabled, 53 Java interaction with, 139–141 stateless server-side coding and, 183–184 310 Hashing Download from www.wowebook.com ptg JavaScript library dashboard visualizations and, 164 loading, 160, 161 JavaScript, mixing in JSNI and. See JavaScript Native Interface (JSNI) JSON. See JavaScript Object Notation (JSON) JSONP, 153–155 overview, 139 summary, 155 JavaScript Native Interface (JSNI) basic usage of, 140–141 browser detection and, 44 getting feed with, 162 hashing with, 142–143 overview, 139–140 Steampunk display widgets and, 143–146 JavaScript Object Notation (JSON) feed data, 161 news reader completion using, 148–153 news reader view using, 147–148 overview, 146–147 weather information and, 158–159 JavaScript Object Notation with Padding (JSONP), 153–155 JavaScriptException objects, 141 JavaScriptObject function, 170 java.sql package, 15 java.util package, 15–16 JCA (Java Cryptography Architecture), 180 Jetty web server, 79 Johnston, Paul, 142 jQuery JavaScript Library, 5, 11, 143 JRE Emulation Library java.io package, 14 java.lang package, 14–15 java.sql package, 15 java.util package, 15–16 JSLint, 282 JSMin, 282 JSNI. See JavaScript Native Interface (JSNI) JSON. See JavaScript Object Notation (JSON) JSONP (JavaScript Object Notation with Padding), 153–155 JSONParser methods, 151 JsonpRequestBuilder class, 154–155 JsonUtils.escapeValue(...) method, 147 JsonUtils.unsafeEval(...) method, 151 JUnit testing basic example of, 231–236 EasyMock and, 240–247 with mock objects, 239–240 MVP code testing, 238–239 overview, 19, 231 test coverage with Emma, 236–238 K @Key(...) annotation key attribute and, 220 resource bundles and, 215 key attribute, 219–223 Keys annotations tricks and, 215 resource bundles and, 212–213 translating error codes and, 216 KeyValueMap class EclEmma coverage test with, 236–238 JUnit testing of, 231–236 module for, 290–291 311KeyValueMap class Download from www.wowebook.com ptg L l10n. See Localization (l10n) Launcher, improved, 37–38 Layouts, complex, 75 Lazy evaluation, 100–101 Least recently used (LRU) logic, 262 Libraries Closure, 5 Flash, 164 JavaScript, 160, 161, 164 jQuery, 5, 11, 143 JRE Emulation, 14–16 Lincoln, Abraham, 144–145, 148, 225, 233–234 LinkedHashMap(...) method, 93–94 Linux client-only GWT deployment and, 297 SOP restriction and, 121 ListBox widgets, 93, 94–99 Live suggestions, 108–112 Loading.texts bundling data and, 277 thread simulation and, 268–269 Localization (l10n) overview, 211 process of, 223–227 summary, 227 -localWorkers, compilation option, 288, 289 Logging in, security and, 185–190 Login button, 67–69 Login procedure, 34–35 Login service, 242–247 Login view, 247–251 LoginFormPresenter class, 60, 62, 64, 66 LoginFormView class MVP implementation and, 60, 64–66 UiBinder and, 70, 72, 74 LoginFormView.ui.xml files, 70, 72 loginServiceMock(...) function, 243–244, 246 LoginView class, 60–61, 63 -logLevel, compilation option, 288 long variables, 13 method, 134–135 LRU (least recently used) logic, 262 M Magic naming, 78 Management Information Systems (MIS) applications, 162 Maps fixed, 173–175 interactive, 168–173 overview, 168 MD5 (Message-Digest algorithm 5), 142–143, 180 Measurement tools, speed JavaScript debuggers, 285–286 overview, 277–278 Page Speed, 283–285 Speed Tracer, 278–280 YSlow, 280–282 Memory leaks, 139 Menus, 41–43 Message-Digest algorithm 5 (MD5) hashing and, 180 hashing with JavaScript and, 142–143 logging in and, 186–190 Messages, dynamic, 217 method, 140 Method parameters, 140–141 312 l10n Download from www.wowebook.com ptg Microsoft Bing Maps, 168 MIS (Management Information Systems) applications, 162 Mock objects testing, 239–240, 242–243, 245–246 Model caching and, 260–261 MVP implementation and, 61 role in MVC, 56–57 role in MVP, 57–58 RPC usage and, 100–101 Model-View-Controller (MVC) design pattern, 56–57 Model-View-Presenter (MVP) code testing, 238–239 Composite widgets and, 95 database-related widgets and, 94–100 design pattern overview, 57–58 Model-View-Presenter (MVP) implementation callbacks and, 59–60 details, 60–66 overview, 59 modelMock call, 243–244, 246 -module, compilation option, 288 Modules application deployment and, 289–291 project structure and, 24–25 Montevideo, 157-159 mouseOver events, 167 moveMarker(...) method, 172–173 Moving files downloading, 204–208 overview, 195 summary, 209 uploading. See Uploading files Multithreading, 14 MultiWordSuggestOracle widgets, 108–112 MVC (Model-View-Controller) design pattern, 56–57 MVP. See Model-View-Presenter (MVP) MyMessages interface, 217–219 N nameBlurCallback attribute, 68 Net tab, 285–286 Network mode, 278–279 new(...) syntax, 151 newCityList function, 131–132 NewsFeed object JSON and, 151–152 JSONP and, 155 NewsReaderDisplay interface, 149–150 NewsReaderPresenter function, 148–150 NewsReaderView files, 147–149 Nixie display widgets, 143–144 NixieDisplay class, 144–146 Non-repudiation, 178 Nonce changing passwords and, 192–193 encryption and, 183 logging in and, 185–190 ... construct, 45