1. Trang chủ
  2. » Công Nghệ Thông Tin

Apress groovy and grails recipes dec 2008 ISBN 143021600x pdf

407 53 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 407
Dung lượng 3,18 MB

Nội dung

Groovy and Grails Recipes Bashar Abdul-Jawad Groovy and Grails Recipes Copyright © 2009 by Bashar Abdul-Jawad All rights reserved No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher ISBN-13 (pbk): 978-1-4302-1600-1 ISBN-13 (electronic): 978-1-4302-1601-8 Printed and bound in the United States of America Trademarked names may appear in this book Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark Java™ and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc., in the US and other countries Apress, Inc., is not affiliated with Sun Microsystems, Inc., and this book was written without endorsement from Sun Microsystems, Inc Lead Editors: Steve Anglin, Tom Welsh Technical Reviewer: Dave Klein Editorial Board: Clay Andres, Steve Anglin, Mark Beckner, Ewan Buckingham, Tony Campbell, Gary Cornell, Jonathan Gennick, Michelle Lowman, Matthew Moodie, Jeffrey Pepper, Frank Pohlmann, Ben Renow-Clarke, Dominic Shakeshaft, Matt Wade, Tom Welsh Project Manager: Kylie Johnston Copy Editor: Sharon Wilkey Associate Production Director: Kari Brooks-Copony Production Editor: Kelly Gunther Compositor: Lynn L’Heureux Proofreaders: Linda Seifert and Patrick Vincent Indexer: Carol Burbo Artist: April Milne Cover Designer: Kurt Krames Manufacturing Director: Tom Debolski Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013 Phone 1-800-SPRINGER, fax 201-348-4505, e-mail kn`ano)juqppkj#A`ep#(`ao_nelpekj6#A`eppk]``jaspkle_o# _he_gHejg#=``Pkle_# C H A P T E R N T E S T I N G ++Ope_gupkle_ oapEjlqpBeah`#ope_gu6Cappejcop]npa`sepdCnkkru#(j]ia6#oq^fa_p# oapEjlqpBeah`#Iaoo]ca^k`u#(j]ia6#iaoo]ca# _he_g>qppkj#?na]pa# ranebuPatp#OdksPkle_#(`ao_nelpekj6#OdksPkle_l]ca# _he_g>qppkj#@ahapa#(`ao_nelpekj6#=ppailpejcpk`ahapa]ope_gupkle_# ranebuPatp#Oknnu(ukq_]jX#p`ahapaope_gupkle_o# ++JkjOpe_gupkle_ _he_gHejg#JasPkle_#(`ao_nelpekj6#?na]pa]jkjope_gupkle_# oapEjlqpBeah`#CnkkruF@>?mqaopekj#(j]ia6#oq^fa_p# oapEjlqpBeah`#Iaoo]ca^k`u#(j]ia6#iaoo]ca# _he_g>qppkj#?na]pa# ranebuPatp#OdksPkle_#(`ao_nelpekj6#OdksPkle_l]ca# _he_g>qppkj#@ahapa#( `ao_nelpekj6#=ppailpejcpk`ahapa]jkjope_gupkle_# ranebuTL]pdtl]pd6++`erWuJ]ia$j]ia% eb$qoan%w 353 354 CHAPTER 16 N MIS C EL L A NEOU S R EC IP ES eb$qoan*l]ooskn`99l]ooskn`%w napqnjqoan*e` y ahoaw napqnjSnkjcl]ooskn` y y ahoaw napqnjQoanjkpbkqj` y y y To use the service in your controller, simply define a property called ]qpdajpe_]pekjOanre_a, as shown in Listing 16-2 Listing 16-2 Injecting a Service in a Controller _h]ooQoan?kjpnkhhanw `ab]qpdajpe_]pekjOanre_a `abej`at9w naj`an$reas6hkcej% y `abhkcej9w `abnaoqhp9]qpdajpe_]pekjOanre_a*hkcej$l]n]io*j]ia(l]n]io*l]ooskn`% eb$naoqhpejop]j_akbJqi^an""naoqhp:,%w ++Qoan]qpdajpe_]pa` oaooekj*qoan9Qoan*cap$naoqhp% naj`anoq aoo y ahoaw ++Qoanjkp]qpdajpe_]pa` naj`an$reas6hkcej(ik`ah6Wiaoo]ca6naoqhpY% y y y In Listing 16-2, the Spring container will inject a new instance of the service into your controller based on the service name It is important to understand that services are created as singletons by default, which means that only one instance of the service will ever be created in your application This is generally fine as long as your services are stateless C H A P T E R N M I S C E LLA N E O U S R E C I P E S (as they should be) If you wish to store state in your service, you may so by adding a static o_kla property to your service: op]pe_o_kla98o_kla[j]ia: The supported scopes are as follows: s lnkpkpula: A new instance of the service will be created every time it’s injected This is the safest option for storing state s namqaop: A new instance of the service will be created for each new request s bh]od, bhks, and _kjrano]pekj: These can be used in the context of web flows only (not discussed in this book) s oaooekj: A new instance of the service will be created for each new session s oejchapkj: Only one instance of the service will be created and shared among all clients of the service Notice the static property pn]jo]_pekj]h in Listing 16-1: ^kkha]jpn]jo]_pekj]h9pnqa This indicates that your service uses Spring declarative transaction management What this means is that all the methods in your service will have automatic transaction management So if an exception occurs before the method is complete, the transaction will be rolled back and will not be committed to the database, preserving the integrity of your data You can still use programmatic transaction management1 if you prefer, but it’s repetitive and pollutes your code Declarative transaction management is one of the most compelling reasons for using services in Grails If you wish your service to be nontransactional, change the pn]jo]_pekj]h property to b]hoa NCaution You should always inject services (whether in controllers, tag libraries, domain classes, or other artifacts) by name as shown in Listing 16-2 and not create a new instance using the jas operator, because in the latter case you will not be using Spring to configure the services Let me demonstrate the importance of transaction management with an example Suppose the Qoan class has a property called jqi^anKbHkcejo that keeps track of how many times the user has logged in to the application I will modify =qpdajpe_]pekjOanre_a to increment that property upon each successful login I will also add a bad line of code dppl6++cn]eho*knc+`k_+-*,*t+cqe`a+oejcha*dpih1*2 Lnkcn]ii]pe_ Pn]jo]_pekjo 355 356 CHAPTER 16 N MIS C EL L A NEOU S R EC IP ES that will cause the service to throw an exception right after the jqi^anKbHkcejo property is incremented and the Qoan instance is saved to the database Listing 16-3 shows the code Listing 16-3 Demonstrating the Importance of Transaction Management _h]oo=qpdajpe_]pekjOanre_aw ^kkha]jpn]jo]_pekj]h9pnqa `abhkcej$j]ia(l]ooskn`%w `abqoan9Qoan*bej`>uJ]ia$j]ia% eb$qoan%w eb$qoan*l]ooskn`99l]ooskn`%w qoan*jqi^anKbHkcejo9qoan*jqi^anKbHkcejo'qoan*o]ra$bhqod6pnqa%++BhqodpdaDe^anj]paoaooekjeiia`e]pahu `ab`ereoekj>uVank9-+,++>]`_k`apd]psehhpdnks]jat_alpekj napqnjqoan*e` y ahoaw napqnjSnkjcl]ooskn` y y ahoaw napqnjQoanjkpbkqj` y y y Now try to log in successfully to the application to cause it to throw an =nepdiape_At_alpekj (because of the division-by-zero bad code) You will notice that the value of the jqi^anKbHkcejo column will not change in the underlying database—even though the session is flushed immediately when calling qoan*o]ra$bhqod6pnqa% Now change the pn]jo]_pekj]h property to b]hoa and you will notice that the value of the jqi^anKbHkcejo column will be incremented Testing services is as easy To initialize a service in your test, simply inject it in the test as you would inject it in a controller The integration test in Listing 16-4 shows how to test the =qpdajpe_]pekjOanre_a in Listing 16-1 Listing 16-4 Testing =qpdajpe_]pekjOanre_a _h]oo=qpdajpe_]pekjOanre_aPaopoatpaj`oCnkkruPaop?]oaw `ab]qpdajpe_]pekjOanre_a C H A P T E R N M I S C E LLA N E O U S R E C I P E S rke`oapQl$%w `abqoan9jasQoan$j]ia6>]od]n(l]ooskn`6l]oo% qoan*o]ra$bhqod6pnqa% y rke`paopHkcej$%w `aboq aoo9]qpdajpe_]pekjOanre_a*hkcej$>]od]n(l]oo% ]ooanpAmq]hooq aoo(`absnkjcL]ooskn`9]qpdajpe_]pekjOanre_a*hkcej$>]od]n(snkjc% ]ooanpAmq]hosnkjcL]ooskn`(Snkjcl]ooskn` `abqoanJkpBkqj`9]qpdajpe_]pekjOanre_a*hkcej$O]ie(l]oo% ]ooanpAmq]hoqoanJkpBkqj`(Qoanjkpbkqj` y y An often asked question is where to put the business logic in a Grails application.2 Many OO experts recommend rich domain models that contain all of your application’s domain logic (business rules, validations, calculations, and so forth) and warn against the anemic domain model antipattern that reduces your domain classes to bags of getters and setters.3 They advise that the service layer should be a thin layer that contains no business rules but rather delegates and coordinates work to the domain layer There is really no set of fixed rules for what should be included in a service layer but rather general guidelines Remember that the service layer is declaratively transactional by default, so you need to take advantage of that and use it whenever you require transactional support The service layer is also useful if you are performing complex CRUD operations on multiple domain classes or if you are reusing code from multiple controllers 16-2 How Can I Use Some of Spring’s Advanced Features with Grails? It is important to know that Grails uses Spring everywhere Controllers, validations, data binding, transaction management, and runtime configuration using dependency injection are all based on Spring and the Spring MVC web framework (dppl6++sss* olnejcbn]iaskng*knc) Spring is an excellent, powerful, and well-documented application framework for building Java Enterprise applications Many times Grails developers might dppl6++sss*j]^^ha*_ki+NA!/=)Sdana)`k)sa)lqp)kqn)^qoejaoo)hkce_)) pk-12,54/5* dpih]-1200334 dppl6++sss*i]npejbkshan*_ki+^hege+=jaie_@ki]ejIk`ah*dpih 357 358 CHAPTER 16 N MIS C EL L A NEOU S R EC IP ES need to work with the underlying Spring model directly for advanced needs This recipe illustrates how to that, and therefore some knowledge of Spring is required.4 Grails uses Spring for dependency injection at runtime Grails’ main =llhe_]pekj?kjpatp file is located at sa^)]ll+SA>)EJB+]llhe_]pekj?kjpatp*tih and is used to configure a Grails application at runtime Take a look at the sa^*tih file of a Grails application at on_+pailh]pao+ s]n+sa^*tih (which can be obtained with the command cn]ehoejop]hh)pailh]pao) You will see the listener class Cn]eho?kjpatpHk]`anHeopajan This class is responsible for reading the main Grails =llhe_]pekj?kjpatp file as defined by the _kjpatp?kjbecHk_]pekj _kjpatp)l]n]i value There are two ways you can configure additional beans in your Grails application for dependency injection: either you can use a traditional Spring XML file by creating a new file called naokqn_ao*tih inside cn]eho)]ll+_kjb+olnejc and placing your bean definitions there or you can define your beans in a groovier way by using Grails’ >a]j>qeh`an inside cn]eho)]ll+_kjb+olnejc+naokqn_ao*cnkkru The latter approach has the added advantage that you can use Groovy code when configuring your beans In Recipe 11-14, I showed you how to upload files in Grails A similar controller (shown in Listing 16-5) lets users upload a file when submitting a new topic to a forum Listing 16-5 Uploading Files in Grails _h]ooReasBknqi?kjpnkhhanw `aboq^iep9w Pkle_pkle_9jasPkle_$l]n]ioW#pkle_#Y% eb$pkle_*iuBeha*ailpu%w pkle_*iuBeha*pn]jobanPk$ jasBeha$#+dkia+^f]s]`+Cn]eho@aik+#'pkle_*iuBeha*knecej]hBehaj]ia%% y eb$pkle_*o]ra$%% naj`anoq aoo y y The Pkle_ class looks as follows: _h]ooPkle_w Opnejcoq^fa_p Opnejciaoo]ca There are many excellent books on Spring For those interested, I highly recommend reading Pro Spring 2.5 by Jan Machacek, Jessica Ditt, Aleska Vukotic, and Anirvan Chakraborty (Apress, 2008); Beginning Spring 2: From Novice to Professional, by Dave Minter (Apress, 2007); and Spring Recipes: A Problem-Solution Approach, by Gary Mak (Apress, 2008) C H A P T E R N M I S C E LLA N E O U S R E C I P E S `abiuBeha op]pe_pn]joeajpo9W#iuBeha#Y y You may have noticed that the code in Listing 16-5 hard-codes the upload location in the controller It seems a better idea to make this location configurable Let’s move the code to a service and configure it in Spring The service is shown in Listing 16-6 Listing 16-6 Qlhk]`Oanre_a _h]ooQlhk]`Oanre_aw ^kkha]jpn]jo]_pekj]h9b]hoa `abqlhk]`Hk_]pekj `abqlhk]`$`abbeha%w beha*pn]jobanPk$jasBeha$qlhk]`Hk_]pekj'beha*knecej]hBehaj]ia%% y y Listing 16-7 shows how to configure the service in Spring by using XML Listing 16-7 cn]eho)]ll+_kjb+olnejc+naokqn_ao*tih 8^a]jotihjo9dppl6++sss*olnejcbn]iaskng*knc+o_dai]+^a]jo tihjo6toe9dppl6++sss*s/*knc+.,,-+TIHO_dai])ejop]j_a toe6o_dai]Hk_]pekj9 dppl6++sss*olnejcbn]iaskng*knc+o_dai]+^a]jo dppl6++sss*olnejcbn]iaskng*knc+o_dai]+^a]jo+olnejc)^a]jo).*,*to`: 8^a]je`9qlhk]`Oanre_a_h]oo9Qlhk]`Oanre_a: 8lnklanpuj]ia9qlhk]`Hk_]pekjr]hqa9 wqlhk]`*hk_]pekjy+: 8+^a]j: 8+^a]jo: Listing 16-8 shows how to configure the service by using Spring DSL Listing 16-8 cn]eho)]ll+_kjb+olnejc+naokqn_ao*cnkkru eilknpop]pe_knc*_k`ad]qo*cnkkru*cn]eho*_kiikjo*?kjbecqn]pekjDkh`an*_kjbec ^a]jo9w qlhk]`Oanre_a$Qlhk]`Oanre_a%w qlhk]`Hk_]pekj9_kjbec*qlhk]`*hk_]pekj y y 359 360 CHAPTER 16 N MIS C EL L A NEOU S R EC IP ES Add the qlhk]`*hk_]pekj property to cn]eho)]ll+_kjb+?kjbec*cnkkru as follows: qlhk]`*hk_]pekj9+dkia+^f]s]`+@aogpkl+ To initialize the service in the ReasBknqi?kjpnkhhan, simply define a property there called qlhk]`Oanre_a, as shown in Listing 16-9 Listing 16-9 Injecting Qlhk]`Oanre_a in ReasBknqi?kjpnkhhan _h]ooReasBknqi?kjpnkhhanw `abqlhk]`Oanre_a `aboq^iep9w Pkle_pkle_9jasPkle_$l]n]ioW#pkle_#Y% eb$pkle_*iuBeha*ailpu%w qlhk]`Oanre_a*qlhk]`$pkle_*iuBeha% y eb$pkle_*o]ra$%% naj`anoq aoo y y You can also reference any Spring bean that is configured during runtime, even if the bean is not declared statically anywhere For example, you can reference the `]p]Okqn_a and Hibernate oaooekjB]_pknu beans as follows: 8^a]je`9qlhk]`Oanre_a_h]oo9Qlhk]`Oanre_a: 8lnklanpuj]ia9qlhk]`Hk_]pekjr]hqa9 wqlhk]`*hk_]pekjy+: 8lnklanpuj]ia9`]p]Okqn_anab9`]p]Okqn_a+: 8lnklanpuj]ia9oaooekjB]_pknunab9oaooekjB]_pknu+: 8+^a]j: 16-3 How Do I Configure My Application by Using External Files? In the preceding recipe, I used the cn]eho)]ll+_kjb+?kjbec*cnkkru file to configure the Qlhk]`Oanre_a service In many situations, you will want the configuration to come from an external file—possibly to have a different set of configurations for each environment or to avoid having to redeploy the application when making configuration changes To so, uncomment the cn]eho*_kjbec*hk_]pekjo property inside cn]eho)]ll+_kjb+ ?kjbec*cnkkru: C H A P T E R N M I S C E LLA N E O U S R E C I P E S cn]eho*_kjbec*hk_]pekjo9 W_h]ool]pd6 w]llJ]iay)_kjbec*lnklanpeao( _h]ool]pd6 w]llJ]iay)_kjbec*cnkkru( beha6 wqoanDkiay+*cn]eho+ w]llJ]iay)_kjbec*lnklanpeao( beha6 wqoanDkiay+*cn]eho+ w]llJ]iay)_kjbec*cnkkru Y The application will now read configurations coming from both Java properties and Groovy ?kjbecOhqnlan files located on the class path or inside the user’s home directory For example, place a file called Bknqi)_kjbec*lnklanpeao inside wqoanDkiay+*cn]eho, where qoanDkia points to your home directory The file will contain the qlhk]`*hk_]pekj property as follows: qlhk]`*hk_]pekj9+dkia+^f]s]`+@aogpkl+ This will cause the application to read the qlhk]`*hk_]pekj property from the external file A common requirement is to configure data sources externally Listing 16-10 shows an external Groovy configuration file (at wqoanDkiay+*cn]eho+Bknqi)_kjbec*cnkkru) that configures @]p]Okqn_a*cnkkru (as originally shown in Listing 12-2) Listing 16-11 shows the modified @]p]Okqn_a*cnkkru Listing 16-10 Externalizing @]p]Okqn_a*cnkkru ++ wqoanDkiay+*cn]eho+Bknqi)_kjbec*cnkkru eilknpknc*_k`ad]qo*cnkkru*cn]eho*_kiikjo*Cn]eho=llhe_]pekj `abajrenkjiajp9Ouopai*capLnklanpu$Cn]eho=llhe_]pekj*AJRENKJIAJP% eb$ajrenkjiajp99#`arahkliajp#%w `]p]Okqn_a*qoanj]ia9o] `]p]Okqn_a*l]ooskn`9 `]p]Okqn_a*qnh9f`^_6domh`^6iai6`ar@> `]p]Okqn_a*`neran?h]ooJ]ia9knc*domh`^*f`^_@neran y ahoaeb$ajrenkjiajp99#paop#%w `]p]Okqn_a*qoanj]ia9o] `]p]Okqn_a*l]ooskn`9 `]p]Okqn_a*qnh9f`^_6domh`^6iai6paop@^ `]p]Okqn_a*`neran?h]ooJ]ia9knc*domh`^*f`^_@neran y 361 362 CHAPTER 16 N MIS C EL L A NEOU S R EC IP ES ahoaw++Lnk`q_pekj `]p]Okqn_a*qoanj]ia9nkkp `]p]Okqn_a*l]ooskn`9 `]p]Okqn_a*qnh9f`^_6iuomh6++hk_]hdkop6//,2+bknqi `]p]Okqn_a*`neran?h]ooJ]ia9knc*cfp*ii*iuomh*@neran y Listing 16-11 Modified @]p]Okqn_a*cnkkru `]p]Okqn_aw lkkha`9b]hoa y de^anj]paw _]_da*qoa[oa_kj`[harah[_]_da9pnqa _]_da*qoa[mqanu[_]_da9pnqa _]_da*lnkre`an[_h]oo9#knc*de^anj]pa*_]_da*Ad?]_daLnkre`an# y ++ajrenkjiajpola_ebe_oappejco ajrenkjiajpow `arahkliajpw `]p]Okqn_aw `^?na]pa9_na]pa)`nkl++kjakb#_na]pa#(#_na]pa)`nkl#(#ql`]pa# y y paopw `]p]Okqn_aw `^?na]pa9ql`]pa y y lnk`q_pekjw `]p]Okqn_aw `^?na]pa9ql`]pa y y y C H A P T E R N M I S C E LLA N E O U S R E C I P E S 16-4 How Do I Configure Logging in My Application? Grails uses log4j (dppl6++hkccejc*]l]_da*knc+hkc0f+-*.+ej`at*dpih) for logging All logging can be configured inside the cn]eho)]ll+_kjb+?kjbec*cnkkru file Grails uses this file to generate the hkc0f*lnklanpeao file, which is required by log4j Logging is configured by using Groovy’s ?kjbecOhqnlan (see Recipe 9-8) Unfortunately, configuring logging by using ?kjbecOhqnlan is hard and confusing (because ?kjbecOhqnlan is not really fully hierarchical but rather pseudo-hierarchal, and each node in the hierarchy is a property) I believe that future versions of Grails should drop using ?kjbecOhqnlan for configuring log4j and instead use XML (with Groovy’s I]ngql>qeh`an) or properties file format Fortunately, if you prefer to use the standard log4j properties file format, you can so using multiline strings For example: hkcf9 hkc0f*nkkpHkccan9ANNKN(op`kqp hkc0f*]llaj`an*op`kqp9knc*]l]_da*hkc0f*?kjokha=llaj`an hkc0f*]llaj`an*op`kqp*h]ukqp9knc*]l]_da*hkc0f*L]ppanjH]ukqp hkc0f*]llaj`an*op`kqp*h]ukqp*?kjranoekjL]ppanj9W!1lY!`wii6ooy$!B6!I6!H%!j!i!j!j  Grails’ default logging configuration defines several loggers (for Spring, Hibernate, controllers, plug-ins, and more) The full stack trace is written to the file op]_gpn]_a*hkc by default You can disable stack trace filtering by using the argument Ì@cn]eho*bqhh* op]_gpn]_a9pnqa All the artifacts in your application (controllers, domain classes, services, tag libraries, and so forth) have access to a dynamic hkc method For example, to log a message at the `a^qc level, you write this: hkc*`a^qc`a^qciaoo]ca Similarly, to log a message at the s]nj level, you use the following: hkc*s]njs]njejc You can also specify different logging options per environment For example, suppose in the ReasBknqi?kjpnkhhan I want to log messages of all levels to the output console when running in development mode, while in production mode I want to log only messages of level annkn and b]p]h to the file system Listing 16-12 shows how to so 363 .. .Groovy and Grails Recipes Bashar Abdul-Jawad Groovy and Grails Recipes Copyright © 2009 by Bashar Abdul-Jawad All rights reserved... Arizona, and Chennai, India—in using Groovy and Grails and thinking in Groovy instead of Java To date, Bashar still gives weekly training sessions in all three places on subjects related to Groovy, Grails, ... should switch to Groovy and Grails and assured its management that after years of Tapestry’s overwhelming complexity, their developers would be delighted to work with Groovy and Grails and would be

Ngày đăng: 19/04/2019, 10:50