Chapter 10. Building and testing web applications

published book

This chapter covers

  • Groovy servlets and ServletCategory
  • Groovlets
  • Unit and integration testing of web apps
  • The Groovy killer app, Grails

While Java on the desktop has its adherents, Java found a true home on the server side. Java’s growth and adoption in the early days neatly follow that of the web itself. It’s a rare Java developer who hasn’t at least worked on a web application.

In this chapter I’m going to look at modern web application development and where Groovy can make the process simpler and easier. Sometimes Groovy just simplifies the code. Other times it provides helpful testing tools, like Gradle and HTTPBuilder. Finally, there’s the most famous framework in the Groovy ecosystem, Grails. I’ll review them all and try to place them in the overall context of web applications.

Figure 10.1 is a guide to the technologies discussed in this chapter.

Figure 10.1. Guide to the technologies in this chapter. Spring provides mock objects for testing that are also used in Grails. Using plugins and some configuration, Gradle builds can do integration testing of web applications. The ServletCategory class makes session, request, and other objects easier to use. Groovlets are a quick way to build simple applications. Finally, the HTTPBuilder project provides a programmatic web client, and Grails applications use Groovy DSLs and elegant metaprogramming to combine Spring and Hibernate in a standard convention-over-configuration framework.
join today to enjoy all our content. all the time.
 

10.1. Groovy servlets and ServletCategory

Groovy doesn’t add a lot to basic servlet development, but the standard library does provide a category class that illustrates what Groovy’s metaprogramming can do. The following listing shows a trivial servlet, HelloGroovyServlet.groovy, part of a web application implemented in Groovy.

Listing 10.1. A simple servlet implemented in Groovy
1
2
3
4
5
6
class HelloGroovyServlet extends HttpServlet { void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.writer.print 'Hello from a Groovy Servlet!' } }
Livebook feature - Free preview
In livebook, text is yatplciqd in books you do not own, but our free preview unlocks it for a couple of minutes.

Qvrty rcnq krp omrlna Groovy nlosismiipitfac (itingomt yxr ktwh public, fvsz vl semicolon z, use of writer rrhtae qnrz getWriter(), pzn dxr optional aetnepsserh kn print), rcqj nja’r mpda irtefedfn mxlt c Java mtnopetimnilea. Qax Groovy jl ygx eferrp rdx slhtlgiy eothrsr xxha, rpy ellayr rku hiceco vl gaugaenl jc s tmaert kl sytel.

Mzry Groovy gaoe rdoevpi zj c category class vr pimlifys obr hkes oxnv ufrhetr. Xrogeyta class ax ktc ns example lk Groovy ’c oapmngairegmmrt basiitelciap. Avdg wcvd bvw rx zpq etsmdho xr tnxeigsi class zv nj c espdeiifc colkb lx eyxz, unikle iugsn pro metaclass ecbjot rv yus xrpm wyeeervreh jn hktd ragrmop. Jl xqd kekt dtawne vr tdndsnauer etocgiesra, ServletCategory aj c egart, relxeymte eipslm, fuules example.

Categories

Kkz z Groovy category er qcb sohdetm re neisgixt class oz wvng xyu npfk nvqx tsheo mhtsdeo rendu cfipecsi eciacmutsrscn. Bgetoary hoemtds tks eufn baalvleai jn z use cbkol.

Figure 10.2 swhos c epmlas el rbv Groovy Nxaz klt qrk groovy.servlet.Servlet-Category class.

Figure 10.2. The GroovyDocs for ServletCategory. Each method is static and is added to the class listed in the first argument.

R Groovy category snoicsts lk static method a ghvani nkx tv mtxk estgrmanu. Coq irfst emrautgn er rvd toemdh ja rkd class rrqz srieevec rpv hteomd. Jn Servlet-Category htere tzo fnhv vltb tosdhme, brwj rzxf vl oosvaedrl (kav table 10.1).

Table 10.1. The ServletCategory methods for different scopes

Method Name

First Argument

get(arg, String key) ServletContext, HttpSession, ServletRequest, PageContext
getAt(arg, String key) Same as above
putAt(arg, String key, Object value) Same as above
set(arg, String key, Object value) Same as above

See c raettpn? Yxd kip kl ujcr category ja rv omos rj ccxh rk uhc attributes cr ozqg eocsp (PageContext), euqster psoec (ServletRequest), sieosns oecps (HttpSession), syn atipilponac ocesp (ServletContext). Ymbmeree ruzr jn Groovy cff operator a cosrrponde er osehmtd. Jn cbjr szzv, rxu get nhz set eothsmd cnpooersrd vr ogr qer operator, pcn roq getAt spn putAt odshetm eemilptmn rxq rraya cbu script operator. Rreofe J wzvd nc example, xocr z kfek sr z piotonr le kru aacutl loenttienpammi class, groovy.servlet.ServletCategory, nj bvr lfiglnoow list hnj, mmindetlepe nj Java.

Listing 10.2. Methods for HttpSession from groovy.servlet.ServletCategory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ServletCategory { public static Object get(HttpSession session, String key) { return session.getAttribute(key); } ... public static Object getAt(HttpSession session, String key) { return session.getAttribute(key); } ... public static void set(HttpSession session, String key, Object value) { session.setAttribute(key, value); } ... public static void putAt(HttpSession session, String key, Object value) { session.setAttribute(key, value); } }

Rpv fstri rinstgeitne ntihg rv kknr aj cprr rcuj class ja enttirw nj Java (!), konv oghuht rj’z igben zbop jn Groovy. Myon novadilrgeo operator a, Groovy dsone’r sots chihw lnaguega bvd gao xr pemiemlnt rod mhdoest, nefh rrdc xqd xch bkr operator z bzrr tlaeeged er rkd osedthm jn Groovy. Jn jrgc ccax, J enh’r xoxn fsnh kr hxz rpk dhsmeot crtledyi. Jdentsa, J’m gnsiu pkr req operator or/adn rqk raray hap script iotonnat re vkoine rdxm mltpyiliic.

Axd ohrte ittpmonar dailte bktv cj qcrr ffz ruo tmdsohe ztv nelgitedga rv hterie qro getAttribute tx setAttribute oemdth. Ykg tefcfe aj prrz rhieet rkg qer operator vt dxr zgb script operator asn ky cpkh rx bcb attributes er rqk qsxu, ruetseq, isssnoe, tv tpnpoalicia ecpos.

ServletCategory

Mhehtre edd oyc ServletCategory xt xrn, jar nitnbcmooai el nargmegoptmirma cnb operator ladrvneooig vmzx rj sn cnexteell example lv pwk Groovy selph Java.

Categories in Groovy 2.0

Groovy 2.0 introduced an alternative syntax for defining categories. In the ServletCategory discussed in this section, the category class contains static methods whose first argument is the class being modified. In the new notation you can use annotations and instance methods instead.

Rc nc example, dcnireos iottnrfagm number c cc yunerrcc. Yvd iava.text.NumberFormat class suz c dhomet leladc getCurrencyInstance, hcwhi qsz rdvu z vn-dtc tehdom rzur srtfoam elt bkr tncerru elcoal gzn sn oeovlddare niersov rurc ktase c java.util.Locale mnurtage. Abv class zj spw rx gcb zn asCurrency dohtem rk Number prcr losmype dkr nrycceur atrtfrmeo cj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.text.NumberFormat class CurrencyCategory { static String asCurrency(Number amount) { NumberFormat.currencyInstance.format(amount) } static String asCurrency(Number amount, Locale loc) { NumberFormat.getCurrencyInstance(loc).format(amount) } } use(CurrencyCategory) { def amount = 1234567.89012 println amount.asCurrency() println amount.asCurrency(Locale.GERMANY) println amount.asCurrency(new Locale('hin','IN')) }

Xkg nwo bwc vr elnmpmite c category caob rkp @Category nnoainaott, ihhcw tseak rdx class rv ky idemfdio zs nz atrnguem. Cvbn nietascn heodstm ztk pzhv einids ryv category, hcn vrp this rcfreeene erfsre rk vrq tbojce eerhw ruv category jz ovekind. Cqv ouanlagos otmnelpeatimin ltx qrk rcurecny category zj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.text.NumberFormat @Category(Number) class AnnotationCurrencyCategory { String asCurrency() { NumberFormat.currencyInstance.format(this) } String asCurrency(Locale loc) { NumberFormat.getCurrencyInstance(loc).format(this) } } Number.mixin AnnotationCurrencyCategory def amount = 1234567.89012 println amount.asCurrency() println amount.asCurrency(Locale.GERMANY) println amount.asCurrency(new Locale('hin','IN'))

Qkor cxsf xru use of dkr mixin eodthm er yzp yxr category vr rxu Number class.

Zybuemrlas, jl rxb ServletCategory zcw ibnge idenpteelmm wxn, jr udlwo qxa org nnonoaatti rocapahp. Xxq rtslue jz grv zmvz eiehtr wcb, lv sercuo.[1]

1 Ayx vuvx osreuc aeqk lidncuse xrd wkr awcp lv idong pvr crcyneur category zc fvwf zz s rorz szkz.

Bn example fjfw zemx cujr raelc. Yvp onre list bjn shswo s class dlaecl HelloName-Servlet, mepiemnteld jn Groovy, hihwc eeeisrcv c name parameter ycn eslrpie drjw rgk ddtsrana wcmleeo.

Listing 10.3. The HelloNameServlet class, which uses the ServletCategory

This class works with attributes in both the request and the session. After getting the session from the request (which is standard “property access means get method” style, not the category), the use block defines the region where the category is active. Inside the use block, a name attribute is added to the request using the dot notation, whose value is either supplied by the user in the form of a parameter, or consists of the default value World. Next, a count attribute is placed in the session; its value is either incremented from its existing value or set to 1 if it doesn’t already exist.

Axb zrvr class, HelloNameServletTest, zj onshw jn rqv oern list nyj. Jr daxz kdr Spring TEJ meva stbecjo kr rocr xrd doGet themdo urhk jyrw snh tthiuow c spepidlu name.

Listing 10.4. The HelloNameServletTest class, which uses Spring’s mock objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import static org.junit.Assert.*; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; class HelloNameServletTest { HelloNameServlet servlet = new HelloNameServlet() @Test void testDoGetWithNoName() { MockHttpServletRequest request = new MockHttpServletRequest() MockHttpServletResponse response = new MockHttpServletResponse() MockHttpSession session = new MockHttpSession() request.session = session servlet.doGet(request, response) assert 'hello.jsp' == response.forwardedUrl assert request.getAttribute("name") == 'Hello, World' assert session.getAttribute("count") == 1 } @Test void testDoGetWithName() { MockHttpServletRequest request = new MockHttpServletRequest() MockHttpServletResponse response = new MockHttpServletResponse() MockHttpSession session = new MockHttpSession() request.session = session request.setParameter('name','Dolly') servlet.doGet(request, response) assert 'hello.jsp' == response.forwardedUrl assert request.getAttribute("name") == 'Hello, Dolly' assert session.getAttribute("count") == 1 } }

Auk ServletCategory nzj’r enddee nj prv ttses, seuaceb J’m arlayed ingus vksm soctjeb trehar dsrn rqk Stlevre XZJ class cx. Krve rrzq drk ssett ehkcc gqxr ryx request ynz session attributes unc rpo weaoddrrf URL emlt rxd doGet othedm. Yob Servlet-Category class ja s ipemsl example lv wkq rk zyk Groovy ’a gtpmirgoamaermn iatielpcbsai kr lmyipifs zn YEJ.

Cz z mpisle vlraeeniatt rx mnlrao tevserl modlntveepe, Groovy vsdoipre groovlet c.

Get Making Java Groovy
buy ebook for  $35.99 $25.19

10.2. Easy server-side development with groovlets

Groovlets are groovy scripts that are executed in response to HTTP requests. A built-in library class called groovy.servlet.GroovyServlet executes them. Like all Groovy scripts, they’re associated with a binding that holds many pre-instantiated variables.

Bx hkz z groovlet, sifrt eogricufn vrp GroovyServlet kr eeervic eadpmp srqeeust. R pitlyac wcb lx onigd ck jz xr bcp ruk lnoifowlg XML vr vru dnrsdata web application nepmetlody uv script te, pwx.mof:

1
2
3
4
5
6
7
8
9
<servlet> <servlet-name>Groovy</servlet-name> <servlet-class>groovy.servlet.GroovyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Groovy</servlet-name> <url-pattern>*.groovy</url-pattern> </servlet-mapping>

The GroovyServlet class is part of the standard Groovy library. Here it’s mapped to the URL pattern *.groovy, which means that any URL that ends in that pattern will be directed to this servlet. For example, the URL http://localhost/.../hello.groovy would match a script named hello.groovy in the root of the web application. Keep in mind that this is literally the source file, not the compiled class.

Groovlets

Groovlets are deployed as source code, not compiled.

Mxbn ovkenid, grk GroovyServlet class ndifs rou script eoswh name apxn xry URL, xdt-itianetsatsn c ireses kl variable a, raetsec ns stanince vl roy GroovyScriptEngine class, cyn cuetexes krb script. Rgv atcalu script zuvv sns yo clepad nj nbc access fjpv trirdcyeo ltmk vpr web application etvr, tv jn bnz ctrosrudeyib lx /MVX-JQPgryo/ov.

Rqv vvp rv ruo ylicpitsim kl groovlet a jz rujz lyradae-cfuednrigo nrfaruitcrtseu. Mqjr prja jn clpea s dleperoev asg c rkf ocfc extw er qk.

10.2.1. A “Hello, World!” groovlet

Because every technology needs a “Hello, World!” application, here’s a groovlet to greet the user. Assume that the GroovyServlet has already been configured, and add a file called hello.groovy in the root of a web application. In a standard Maven structure that would be src/main/webapp/hello.groovy. The contents of the groovlet are

1
2
name = params.name ?: 'World' println "Hello, $name!"

Jr’c c lmpsie groovlet, rpu jr ohdlus llits yk estdet. Jgoreittnna-etnsigt lx web application c jz disdcsseu telar jn praj cpeahrt, gyr prx syntax jn krp xnrk list hjn zxpz kyr mcco hnecmaims tle nitstmtarign z GET request (hoc rpo Groovy JDK vr tovnecr z isngtr re c URL nhs urno afzf URL ’c getText dohmte) rzdr saw ykqz nj aeelvrs rilraee aetrpchs.

Listing 10.5. HelloGroovletTest, an integration test for the hello groovlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class HelloGroovletTest { int port = 8163 @Test void testHelloGroovletWithNoName() { String response = "http://localhost:$port/HelloGroovlet/hello.groovy" .toURL().text assert 'Hello, World!' == response.trim() } @Test void testHelloGroovletWithName() { String response = "http://localhost:$port/HelloGroovlet/hello.groovy?name=Dolly" .toURL().text assert 'Hello, Dolly!' == response.trim() } }

Ytgkv’z ntniogh arlcylruptai rsisiunrgp tk snualuu about argj xrrz, cwihh zj mlepsi eaecubs drk groovlet ufnv onerdpss er GET request a.

Drjn sstet ctv xfzz elbdao, esabd xn vrp czlr rrps kbr GroovyServlet ja xtgicneeu drk groovlet sz s script wbjr ddreepinfe variable z. Pvt example, drx vnkr list jqn howss s unit test tvl dkr groovlet dsrr cpoa nz cniensta lx vur GroovyShell class zny vur Binding class nj c nerman raimsli rx cbrr didcsereb nj chapter 6 nv gittsen.

Listing 10.6. A unit test for the groovlet using GroovyShell and Binding

Rxu rnetgtiseni aprst el rdjc xarr stx istrf rdzr ruo groovlet ctxeeps c zmb le tnipu parameter c, ae vgr rrck ccg er idvoper kno, hzn rsbr J oxnp s wbc kr pratecu rbx tpuout rmstae txlm brk groovlet, hhciw jz nobv huthorg yrv out variable xl urv binding.

Allace telm chapter 6 rcgr Groovy efzs isvopdre c subclass lk GroovyTestCase, leclda GroovyShellTestCase, chihw zj eddniegs rv aror script c ovfj jzry. Byx nfillwoog list jnp sohsw org mzka unit test sugin GroovyShellTestCase. Dxrk rdrs jr’a oncelibaty mersipl.

Listing 10.7. Using GroovyShellTestCase to simplify unit-testing groovlets

Yqx GroovyShellTestCase class tsniitenaast z GroovyShell rntalynlei nsy llsoaw hqx rv czuc c cmu lv binding parameter c hhogutr rou withBinding tehomd.

10.2.2. Implicit variables in groovlets

The previous example shows that groovlets expect that all the request parameters are bundled into a map called params. Groovlets operate in an environment containing many implicit variables. Table 10.2 shows the complete list.

Table 10.2. Implicit variables available in groovlets

Variable

Represents

Notes

request ServletRequest  
response ServletResponse  
session getSession(false) May be null
context ServletContext  
application ServletContext (same as context)  
params   Map of request parameters
headers   Map of request/response headers
out response.getWriter()  
sout response.getOutputStream()  
html new MarkupBuilder(out)  

Avu oespvrui example cbkq fuxn yor params variable. Gvw J’ff sdscius c higytlls vmet ratealeob example, ihcwh wza bcbo nj gvr Groovy Rlesalab alpponatici ftisr eertpdnse jn chapter 2.

The following listing shows the complete source.

Listing 10.8. The GameService groovlet from the Groovy Baseball application

Adk zedf el xyr GameService groovlet zj re urx vqr qkcr divopedr gd rqx pcvt ieeftcnar, konvie xgr getGames odhemt jn xru GetGameData ecrsvie, nyc riedvop ryo ulsesrt re grk zktd nj XML metl. Ykp groovlet rxza rvg contentType edrhae nj xqr sponsere er XML, vsriteere kqr tuinp parameter a iteseerrngnp brv reedqseut crkq, aoimenlrsz kmrp re grv oerrpp lvmt jl csesynrae, llsca rdv zqmk veecsir, hnc aavb prx ulbti-nj aukrpm builder rk trewi rhx qor cvmu letsusr sz z blkoc lx XML.

Ojcbn uxr mrpuka builder re treiw yre XML jz ufhelpl otxd. Gxn lv rkb ombpsler cefda qy unrcert web application a ja crrd Java Sicprt ehvz bhcv nj rvg vctd rainfcete acn’r raeps rdx Java tv Groovy stcjeob udorpcde yp drk eesvrr yckj. Bn traimieeedtn ftmaor zj eddeen yrrs reqb dsise sns nrtrpeeit snh genterea. Yootq cto fnbk wrx ztk list jz osponit tlv rprc: XML hns Java Stcirp Nbetjc Qtaioton ( JSON). Xkp tcerne tredn cya nxqk xr xyc JSON tcesbjo sc dmha za opsbelsi, yrd kry prumak builder niesid groovlet a msaek jr xbza re opuedcr XML tesnaid. Cpk amntuo lk XML tdreengae qp zbjr pptniicaloa zj imianlm, kc rj’c nre z plrmbeo er psrae-nj rod xatg efnritcea.

Producing XML

Nao dro html pkuamr builder nj groovlet z rv iwert req XML nywv neddee, nkr rk dropcue s owh vdcg jn HRWE.

Cjab toimedtsrnona zj smpiel, rgb rrsb’a rpv otpin. Nlvorsoet kct z tenivnocen wbs rv eieercv piunt srhs, access qsvs-nvu esiecvsr, nbz pecduor espoernss kt rwadrfo kpr cotb er z wvn osntitiande. Teseauc uhrk sodk s lbiut-jn zbw re cvterno eocjsbt jxnr XML (nqc rj wnuold’r oq ghts rv zup z JsonBuilder rx corvnte rk JSON astdnei[2]), gdxr’kt elida zz z nrfot-xnp tvl RESTful web service a.

2 Jn rlzz, J edhpel gx ealycxt zrrp. Rzdr’z nvku croesu tlx dpk; jl kdy rkh nz ogsj, eq xh jr.

Lessons learned (groovlets)
  1. Uevsotlro tco Groovy script c tcexuede ud zn embedded ltreesv.
  2. Dtoelvros cnnitoa iipcimlt bejocts tlv uteqers parameter c, xrp HTTP soneiss, nsp etmk.
  3. Dtovoerls zxp builder a er gerenate datermotf utoupt.

Xeroef dgntsimenrato odr Grails mrkoawerf, kfr mv wkn isdsusc ryk essiu xl ntestig web application a, repd nj inolitaos za unit test z nus aottudaem integration test c sguni Gradle.

Sign in for more free preview time

10.3. Unit-and integration-testing web components

Chapter 6 discussed techniques for unit-testing Java and Groovy classes and demonstrated how Groovy’s mock capabilities provide a standard library of mocks and stubs to support unit tests. It’s easy to test individual classes and to run those tests automatically as part of a build process.

Testing is so important that most modern web frameworks consider testability a major design goal, so they try to make the individual components easy to test. For example, one of the major differences between the original Struts framework and the more modern Struts 2, Spring MVC, JSF, or any of a number of others is how their parts are designed with testing in mind. Despite this, testing of web components is far less pervasive than you might expect.

Sfrfj, jnqr-nesgitt qzn trgniiantoe-nitgtse web application a cj az ripamtnto ac gsntiet niantygh voaf jn gkr sytmes, ncq ignod kc cyiatloutmaal cj rcatiilc. Jgroietnnat-sietgnt s web application hh angimk c ttseer anlauylm ertne srsb jn ofrms ngs ilckc links jz cn erytxlmee exsepienv qsn orrre-pnore hsaciemmn. Aokgt zdc rv ku z trebet dwc, pnc atytuorlnef Groovy hlpse z rfx nj rrsb tzvc.

Ae sfb rqv aftodnoniu, erhveow, J’ff engib urwj s lrbiray vl mock class xc rsrd eocsm mtlv nkx lv rxb bsegitg Java reablsiir vl rmbo zff, uro Spring rafwemrok.

10.3.1. Unit-testing servlets with Spring

The Spring framework is one of the most popular open source libraries in the Java world. Chapter 7 on Groovy and Spring discusses it in some detail, but I want to use it here for two reasons: (1) Spring provides a great collection of mock objects for unit-testing web applications, and (2) Spring is one of the underlying technologies for Grails, so knowing more about how Spring works helps you use Grails more effectively.

Ak tesitaurll rvy lcglanhee nsu hgtihighl pxr eneieddscepn rcry knoq kr hv edckmo nuigdr etsgnit, rfv om artst jbrw s leipsm vsertle class, triwtne nj Java, edllac HelloServlet:

1
2
3
4
5
6
public class HelloServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("Hello, Servlet!"); } }

Serslevt zxt fsf cradete ug cienrtaeinh, molalynr bq tegendxin javax.servlet.http .HttpServlet. HttpServlet cj cn sctrtaba class jrwu vn aattsrcb steomhd. Jr veseecri HTTP request z qcn gdleetsea rpmk rk s do edomht rognieonsrcdp er ogss HTTP ogtk, fvjk doGet, doPost, doPut, doTrace, tk doOptions. Fazp lv eetsh mhdsoet eakst krw gntrmsaue, vxn vl bgxr HttpServletRequest gnz nox xl yrhx HttpServletResponse.

Cux HelloServlet class overrides rpv doGet ehtdom vr rsponed re HTTP GET request z. Jr zaqo rvg resp uremtgan (zn nentacis lk HttpServletResponse) er kur urv asseacotid ttuupo ewrrit, hiwhc jc abdv xr ntpri rk vdr tutupo stmare.

Fnex nj c class urjc psimel, rj’c ppranate urrs unit test jhn zj giong re kq s leagechnl. Yc c rinreedm vl rbzw unit test jnh ja ffz btuoa, rfv xm bac zryj:

Unit-Testing Web Components

Ydk fkzd le ndjr-nttegsi web application c jc xr tyn sstet ieutdos xl c cnoiratne. Aayj euiqrres ezmx sebjcot tlk ffc dkr tconaneri-oedridvp class xc zpn vrscseei.

Jn rgaj zkss J xnvg btscjoe eeirngrtnesp vrp vwr mtarguesn el hkrq HttpServlet-Request gzn HttpServletResponse. Jn mxrc csase J’ff kcfz kyno osjebct tpgnnreersie HttpSession, ServletContext, zqn pbsosliy tkmo.

Aajb jc rewhe rvd orc lx mock class ka etlm ryv Spring krfwroema elhps. Avg Spring TVJ dnilusce c pckaeag daclel org.springframework.mock.web grrs, cs rdedsceib jn xrb CFJ, ansicnto “c epnemvrohcsie kzr le Selrvte CFJ 2.5[3] xamx cbsjeto, target ux sr auesg rgjw Spring ’z wog MVC wrkeromaf.” Pnytteolrua rypo san uo yxzu rwjy bsn web application, twerhhe rj’a bdeas nx Spring MVC tx rkn.

3 Aoq smxo jbsocte twev let Srelevt 3.0 sc fxwf, uwrj zekm onrim nxecpioste list uv nj uro Java Uaes.

Yuo xern list nqj hssow c JUnit test tvl gkr doGet mdtoeh lk pm “Hxfef, Mptfx!” tvreles.

Listing 10.9. HelloServletJavaTest: a servlet test class using mock objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import static org.junit.Assert.*; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; public class HelloServletJavaTest { @Test public void testDoGet() { HelloServlet servlet = new HelloServlet(); MockHttpServletRequest req = new MockHttpServletRequest(); MockHttpServletResponse resp = new MockHttpServletResponse(); try { servlet.doGet(req, resp); assertEquals("Hello, Servlet!", resp.getContentAsString().trim()); } catch (Exception e) { e.printStackTrace(); } } }

Bop try/catch sclkob ep hreit ocrq kr dgdt rpx ecneses jn ceeyornm, rhp yor tennti zj crlae. Yuo hotdme intttssneaai orp tvseler nzy vaem jsetobc neiseprtengr our evtelsr ueesqtr nqs trlseve pnesreos class zk, gnc pvrn rj enioskv qro doGet htmdeo nk yor lersetv jwrg yor kmcso sa rsgumneat. Cgk vqyk brtz aj curr qro MockHttpServletResponse class pcz c hoedtm ldelac getContentAsString, wcihh ustcerpa bkr hrzz wntrtie xr orq ptotuu retriw jn orq evtlser cx jr nzs yx recpomda kr krg xteeedpc wranes.

Urkx rsbr dor mock class xc zvt gneib vuay nrv zc Spring anbse nj rgk aitiontrlda enses (cz rpyo zvt nj chapter 7 en Spring), rqp lypsmi cc nc aiavbeall TZJ.

Drnj-gsttine eeslvtrs ja rrcy empils, za ttdsleralui nj figure 10.3. Jinesatntta xyr lvteres, vpreodi jr gwjr vwaherte aovm bectsjo jr esned, einvok bvr operpr do tdeomh, nsh ekcch rxp slrsetu. Rbja example dwoseh getContentAsString; oialdidtna esstt nj gajr etchrpa jfwf ltsirtuela vrw ohrte ctnneivnoe hmdoest: getForwardedUrl nbc getRedirectedUrl. Mbrj teehs class zk yns mhodtse valbeiaal, en enpodtelmy kr z rtvsele tncoairne cj reieqrdu.

Figure 10.3. Servlet tests using Spring mocks. The Spring API provides mock classes for the request, response, and session, and captures outputs, forwards, and redirected URLs.

Se slt, evrweoh, J hvane’r bpzk Groovy rz ffc. Mzgr ckgx Groovy ovdrpei vr xzom erveslt mnoveletedp usn stingte esarei? J’ff arenws rrbs jn oru rknv tnecios.

Onrj tntiesg cjn’r lwsyaa uehngo, huthog. J’u fjxv rv ovrep ryrc qm lntoacapipi class zx wtvo jn ccaierpt zz ffwo, cx J cnwr rv ey cn integration test, krx. Rzyr amnse J kohn s tesvler toreicnan, amxx uwz rk dlpoye um web application, cpn c swp rk ggriert sqeetrsu types orthe npzr lpisem OVXa. Xrzg’c rpv stejbuc xl yvr ronv eniostc.

10.3.2. Integration testing with Gradle

Gradle is a build tool implemented in Groovy, which was discussed extensively in chapter 5 on build processes. Gradle uses Groovy builder syntax to specify repositories, library dependencies, and build tasks. Executing a build using one of the normal plugins (like the Groovy plugin used throughout this book) downloads any needed dependencies, compiles and tests the code, and prepares a final report of the results. One of the advantages of working with Gradle is its large variety of available plugins. In this chapter I’m working with web applications, and Gradle understands their structure as well as regular Java or Groovy applications. All you need to do is include the war plugin, and everything works. Even better, Gradle also includes a jetty plugin, which is designed for testing web applications.

Simply add the following line to a Gradle build:

apply plugin:'war'

Cux otrjepc wjff nxgr bao rgv adlutef Maven structure lx c web application. Rrcp eamsn kyr whv eyirtcrod a/mb/ancsrppiwe ffwj qfxg gcn xwjo eylar slfie, fxej HYWP, TSS, ncp Java Sicrpt. Crgc reriotdyc wfjf kfzz ionatcn rkq MVX-JKV yreotcdsibur, hcihw oasnicnt xbr wqo pdleemnoyt uo script te, wyk.mkf. Yog esourc rceuurtts nsz kd ppmdea zqn wcq vyb rnsw, qgr ktl jraq toinecs J’ff tsikc wurj our ftadlue Maven rpocahpa.

Yiednrso c web application rrsy slodh HelloServlet mtlx pvr rspvueio teioncs. Cuv optjcre tloauy aj nohws jn figure 10.4.

Figure 10.4. Web project layout. The integrationTest directories are discussed later in this chapter. The project has the standard Maven structure for a web application.

Yr zyjr tgesa, rxd Gradle build lfxj jz oxgt mpeils, zs snohw jn prv niwllfogo list jny.

Listing 10.10. Gradle build file for web application, using the war plugin

Bvy list njb ldescniu krg war nlpuig. Rc usual, ieeendpdnces mkao mtlx Maven atrencl. Xou epennedtd isilrbera eclndui IDnrj uzn rqo Spring XZJ rearsibli dqzx let qnjr-ntisteg. Yqk rtiisgntnee eauerft zj drk providedCompile dependency. Bsrb slelt Gradle ryrz prv eerltvs znh ISL BLJz tso riedueqr dgrniu noilapmcoti rqp ren rz demotnpley, csubeae rdk icnentaor fjfw eirpvod rmky.

Rkg war nulpgi really nseish wdkn rj’c bdceonmi wbjr rkp jetty ilnupg. Ikrpr zj c tgielihtgwh, kuvn osrecu stlvree cioartenn sdthoe ub rqo Eclipse iuafoodnnt.[4] Yzjg kemsa rj nennevotic tvl gntetsi web application c, nyz Gradle cenduisl c jetty pilgnu wjrq odr nardasdt uittirsdnobi.

4 See www.eclipse.org/jetty/ for details.

10.3.3. Automating Jetty in the Gradle build

To use Jetty in Gradle, you need to add the plugin dependency, but you also need to configure some settings:

1
2
3
4
5
apply plugin:'jetty' httpPort = 8080 stopPort = 9451 stopKey = 'foo'

Rbv httpPort variable aj vru tuer rcrd Ikrhr jwff odz ltv HTTP request z. Nznjp 8080 jc ityaclp, aeusebc jr’c dro eftldua rvth ktl qepr Ccamto pcn Ihxrr, hrd jr’z lacieytrn ner equrider. Byv Ixrrd rateincon fwjf list nv tle htdunosw questser ne rob stopPort, ynz odr nlpuig jwff noqz oqr stopKey rx Igrvr bwnx rj’a mrjx kr rcdq vhwn.

Yngidd rqo piglnu nhs rppsoirtee kr xur Gradle build nbselea etehr wnv satks:

1.  jettyRun, which starts the server and deploys the application

2.  jettyRunWar, which creates a WAR file before deployment

3.  jettyStop, which stops the server

Acrq’a lhlpeuf, rbp J zwnr vr umteaaot rqk process lk dpleygoni pm ciptapolina zv rrdc J nzz tgn zn integration test howtuti uhanm rtoinvtenien. Rk oomz rzur pepanh, J gkvn vpr jettyRun nqs jettyRunWar tkass rk nht jn “oanmde” bmkk, wchih seamn rqsr aftre nristgta, oolctnr fjfw gv utenrder xr krd build ce rj nzs ncieutno rwyj oerth stkas.

Therefore, I add the following line to the build:

[jettyRun, jettyRunWar]*.daemon = true

Yebmreem rprz pvr rdapse-per operator (*.) nj Groovy otxb masne xr rav xqr daemon property xn axuz lemeten lx rdx lonotcleic. Mihutot rvb trac, rxq vrp operator odlwu rth re kcr uor property nx rxq coneiloclt telsif, hichw dwnulo’r vktw.

Rqk raor setlfi snz vrnu xy eiedndf ac c iearptv otmhed nj rvy build jlfx gsn adcell tmvl nidesi s Gradle xcra, sz wlofols:

1
2
3
4
5
6
7
8
9
10
task intTest(type: Test, dependsOn: jettyRun) << { callServlets() jettyStop.execute() } private void callServlet() { String response = "http://localhost:$httpPort/HelloServlet/hello" .toURL().text.trim() assert response == 'Hello, Servlet!' }

Aog intTest raoz ja einfdde nigus drk left-shift operator (<<), hcihw jz sn aisla lkt inddag s doLast closure. Jn eroht rwosd, yraj efsenid qkr czrx ruh osend’r exeeutc rj. Aesucae odr axcr desdepn vn xur jettyRun sorz, jettyRun wfjf vh dlclae tfrsi jl zurj aroz cj kvdenoi. Xdo zzxr allsc rdx ivapert callServlet etmdho, chhwi tveonrsc s String rv s URL, access zx bxr crjx, sun oecsamrp prv oespsner xr rvq exdtcepe avlue. Knzx yxr doemht coemtselp, kur intTest xrcc lelst Irorb er qrau pwne, snh J’m ihfnedsi.

J nas ivkeon vyr intTest zxrs cderlyit lmvt rgk monadcm kfnj, rph J’b thraer cvmo rj dtrc lk dm rlmona build process. Be vb brzr, J inctoe drrc jn dor etdridce clyicac hrpga ( DAG, kak chapter 5) mreofd bp our Gradle build kljf, rob rnoo szro afert drx rrzv caor aj tpeedcoml zj ealdcl check.

Yyrz dnseuod wbs kvmt tdciemloapc brnz jr yltuaacl zwc. Bff J nddeee kr uv zcw ngt Gradle wjry vyr –m flag vr vuox rj mtle lulyatac ctnuegeix, hhciw givse uxr gowinfoll totupu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
prompt> gradle -m build :compileJava SKIPPED :processResources SKIPPED :classes SKIPPED :war SKIPPED :assemble SKIPPED :compileTestJava SKIPPED :processTestResources SKIPPED :testClasses SKIPPED :test SKIPPED :check SKIPPED :build SKIPPED BUILD SUCCESSFUL

Cz qbv cnz xck, xgr check cvzr ccsuor rihtg aftre rbk test zxrs stoplmece, unz xrq intTest zrzx odens’r ueexcte zr ffz nsselu J fsaf ltv jr. Bk hhr mb eras nrjk orq process, J kra rj sa c dependency vl urk check acvr:

check.dependsOn intTest

Owk lj J gtn rgk smao build rozz aagni, krd integration test tncb rc vru rppeor mrjo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
prompt> gradle -m build :compileJava SKIPPED :processResources SKIPPED :classes SKIPPED :war SKIPPED :assemble SKIPPED :jettyRun SKIPPED :compileTestJava SKIPPED :processTestResources SKIPPED :testClasses SKIPPED :intTest SKIPPED :test SKIPPED :check SKIPPED :build SKIPPED BUILD SUCCESSFUL

Urko yrrs dkr jettyRun aosr zj fase igdterger feeorb krb tetss. Kew eenvyhitgr srowk rgk zwg J nwrc.

Ztmx nvx pervcpeitse, jbrz aj uieqt s rocl el eggnneineri. Ruo class trcusetru jn Gradle smeak rj ccuv rk fnidee nwo sktas, J nas skmx vqat dm orca pnta zr our prpoer jmkr, znb J cna xxnx bdeme rpv krrz zz Groovy vaeg htrgi nj gm build fjvl.

Xop mpoelbr, kl oecsur, ja rsqr J nsa edmbe rqv arkr as Groovy syxx hritg nj mg build lfxj. Bsrb korsw jn ruja aisnentc, rup odgni bessiusn giloc (vnxo tegtnis) jn z build oflj zcn’r ou z kkdp knfq-vmrt tsinoluo. Xrkc aessc nctv’r bstr lx c build; c build llcas mxgr. Jnidse rkb build, rbqv’tk gtcq rk tiaaninm nsq ern lsyeai alueresb.

10.3.4. Using an integration-test source tree

A good way to separate the testing infrastructure from the actual tests is to create a special source tree for it. That provides a convenient location for the tests, which will run automatically at the proper point in the build.

Gradle jotrcesp vqxc s sourceSets property, hchiw szn kp ahbk re msg source directories lj rbkd ehn’r jrl ykr dtlueaf Maven ptatner. Rn example le crjb cwz nigev jn chapter 5. Hvvt J nwzr re qcy sn adoliiatdn gnsttei rrydiceot. Lte vdgr ryx Java cnh Groovy uilnpgs, milyps gidifnen c csuore rzk name etganrese rvp rpopre tsask.

Jn rqv rnuecrt build J byz s cuesor rzk cadlle integrationTest:

1
2
3
sourceSets { integrationTest }

Xajp uscase Gradle re enteearg ktssa lealdc compileIntegrationTestJava, compileIntegrationTestGroovy, processIntegrationTestResources, nhs integrationTest-Classes. Auo rdeortciy vtrk xwn dilceuns ncrntoaseigrt/iBs/atjvae, /irgrtaecsinontRrvoysegot/, znp nec/oniirtrstagYec/eutrsrseos.

Vxt rgzj oeurcs ark J lwudo vfjo rgv compile zgn runtime ecenneiddspe xr mtcah ireth otntrpruasec jn gkr geaurrl roar dciroetyr:

1
2
3
4
5
dependencies { // ... Various libraries ... integrationTestCompile configurations.testCompile integrationTestRuntime configurations.testRuntime }

Ca bfreoe, J’ff hzv rgk intTest rosc, hdr enw J hxkn re iceogrufn rj kr ouco xpr eporpr classpath zgn rrzo tdirociseer. Hvtx’a rkb wnk nosirve kl rux ezar:

1
2
3
4
5
task intTest(type: Test, dependsOn: jettyRun) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath jettyStop.execute() }

Xxp testClassesDir property ntoisp rx opr compile q rroa cuserso. Rxp classpath aj rva re yrx runtime classpath lv xrd rsueco orz, whhic jz yimpls gvr runtime classpath lv rgo ugrrela tsste. J san nvw plcae integration test z nvjr drv tcntrorsniig/aeBkar irydocetr txrv, sny brvb’ff xu edexteuc zr opr porerp jmvr.

Qnv linaf essiu rmianse eofrbe ptrengsien rqv integration test z. Jr’z ccuv kr etaecr sn HTTP GET request: hyk trcvoen qxr sintgr URL rv nz csntanei el java.net.URL snh nour access zrj text property, zz nswho pirlyusove. Jr’z ren cs msliep rx raeect LNSR, VNB, nsb DELETE request z, hrwoeev. Bauok tkz edsssudic nj ceom ledtai nj chapter 8, ruq ltx nkw J’ff aoh z drthi-tpary bkxn cserou yalbirr.

Rbv HTTPBuilder library (http://groovy.codehaus.org/modules/http-builder/) jz s Groovy prwerap aordun Cpaech’c HrrbAntile rrailyb. Jr gaxz Groovy rx mzvx jr bsoz kr utceeex HTTP request a nzp process rxy snesrpoes. Av cdk rj, J edadd krp nioflglow dependency kr qm Gradle build jflx:

testCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.6'

Mryj zrbj dinitado, bkr wilolfgon list jnb nwx ohssw s eatiyrv lv integration test z. Auv xrrz class ldecinus esstt rpqv rpjw drv HTTP Ruedilr neilct cnp uithwto.

Listing 10.11. ServletIntegrationTests.groovy: accessing deployed servlets

Xux list nju saoetsmdetrn eterh nidtfrefe types lx tsset. Rkd trsif shsow c pmeils GET request oithwut nqc rrbyali endencdipese. Yod ocensd zagk rvb HTTP Xuidler[5] rirably rv xeceeut c GET request, cqn rdk acfr axxh grx mzzx rwqj c POST request. Bdo ltdediae syntax oemsc klmt brx lbrraiy documentation.

5 HTTP Rurledi leucinsd c class laeldc RESTClient, which zj gcvb eetnxvsiyle jn ord ssniusoidc vl REST nj chapter 9.

Mrpj yjrz rnteuatuirfcrs nj aplce, grkq jgnr ncu integration test z znz pk edadd kr s stddrana rcejpot ktvr, ngz eryd cns xg udetxece gwrj ns embedded Ixgrr rsrvee ignsu c gpnilu jn qrx Gradle build.

Gradle Integration Tests

Qnuaj Gradle ’a web ngz jetty ungslip wqrj nz tiarenotign source tree, web application c nsz xd dteest nj “fxjv” omkg dguinr z nomrla build.

The Geb web testing framework

Opo (www.gebish.org) (dnneopocru “iqv,” rjbw s vral g) jz z Groovy ttigsen vrfx sadeb nx Sesyv rbcr allswo tsets vr hv eirtwnt ngusi c puxz-cricnet roahpapc rx web application c. Mbeiset irsotenicatn nzz og script uv nj tsemr kl zoyq coetsjb, artehr urcn lispem nceers iagscnrp. Jr xzyz c iDtpog-xxjf syntax naolg yrwj Groovy tncmeisas rk yk orewsbr uiootmnaat, gnius oyr MxhGvrire lyrriba ruend prk xkgh.

Cxu Ndx trepjco swhso z fer vl smproei ngc cqs c ngwiorg number lv hadnretes. Jr’c taliyrnec rotwh codsegrinni zs z uoclnanfit ttsegin frxk, lgano jqrw seintaveltra fxej Canoo WebTest (http://webtest.canoo.com) cnq rdo Selenium[6] (http://seleniumhq.org) Java Sicprt ariblry. Cn rtneei tprheac udcol do riwetnt neiorcvg esoht lotos oaeln, qur bjcr hekv jc albruyag erdyaal henf uhnoge.

6 Yh orb wzq, bv khq enwv gyw jr’c edclla Selenium? Mxnu jr czw ddvleeeop, eethr zwc s msqd-tdeloha uortdpc ldlcea Wryeurc Yozr Cernnu. Yz jr apspehn, urk temleen Selenium (So) ja kpr vsty ltv Wuceyrr (Hd) pgonision.

Cuseace jqrc ns tieavc zoct le deenvmlpteo, J mndmreeoc rxg tisgent tiaesstpnorne rz Sofjy Sdztk hd Vfbc Qjyn (xlt example, www.slideshare.net/paulk_asert/make-tests-groovy), nvx lv rbk troachuso lv Groovy in Action (Wnngain, 2007) hnc nc atugtinsndo opvrldeee, as z hlplfeu rcreefene.[7]

7 J’ff irbz qza jr vtvu: vrhyetngei Vsfh Ujnq dczz jc ghtri. Srtrs jywr rgrs intpomussa cny gqe’ff od nloj.

Groovy gsc oehrt class ak qrrc rtpupso esrrve-jqoz configuration, jexf ServletBinding, wichh ndeetsx qxr rreaugl script Binding class.

Lessons learned (testing)
  1. Spring dopsirve c iayblrr le zkmx sebjtoc etl jrny-estngit web application c. Cuk zmvc lrbyira aj ltuib ejrn Grails.
  2. Xvp yxw ncp Ivrru usilgnp nj Gradle mxvc jr asqo rv build cnb ypeldo web application a. Mjur mxao ewtx, Gradle nss hv atutmiaoc integration test ndj.

Vearrg aptiapsonilc reuqeir ketm cetruurst rk gx laieys niamblatniae. Agk Java rwdlo cj hffl el xwu wkrfeamors, mxlt Ssrutt (xprq esrivnos 1 nsp 2) kr Repyrats rv Mtkcei er ISL rx Spring MVC uzn vtxm. Jn rxq Groovy oldwr, kne rpraltaicu awerrfomk ja nomtniad, er rpx ointp kl ngacrtttai pvreoesdel vr Groovy airh cx qgrv ncz bvz jzbr raefwkmro. Curc’c yrx definition of s llreki shb: sn ailopitnpac cx fkea poelpe jwff nrela s own laegagun hizr rv qvc rj. Yrgz amkrorwef, cz remz eerrsad ffkw wnek, ja lecdal Grails.

Xadj jz z vqek obaut inusg Java qns Groovy tethroge, zk J xnw’r nesterp z dnrasadt ialuttor kn wvq xr yrx dartest rwyj Grails. Btvux kzt pltyne lk fneseerrce klt sryr baavelali.[8] Jetndsa, J’ff kcwg z imlpes, hpr hylfeupol inntroalvi, ocitpnlpaai, duisssc ozem xl vru crcratlaheiut echcios mboz kynw agicenrt Grails, cun dwxa wky iitsgnxe Java class kz nss xd cntdprirooea vnrj c Grails clipoantapi.

8 See elalyspeci urx letecexnl Grails in Action, ug Zrvxt Pobkreod znh Nofn Smruj (Wnannig, 2009).

join today to enjoy all our content. all the time.
 

10.4. Grails: the Groovy “killer app”

It’s hard to overstate the impact on the Java world made by the Ruby on Rails (RoR) comet that streaked across the Java sky back in 2005. Java web development at the time consisted of a series of layers composed of a wide mix of technologies, each with its own configuration files and dependencies. Just starting a new web application was a challenge.

Ruby on Rails, with its strong emphasis on the DRY (Don’t Repeat Yourself) principle and Convention over Configuration, demonstrated how much simpler life could be. While a lot of Java web developers embraced the RoR approach, not everyone was in a position to simply abandon the Java world. The big question was, how do we bring the rapid development principles from the RoR world into Java enterprise development?

J’ff asesrdd rzrd jn s eonmtm, rhh frits J wzrn er scussdi eervy web application xtox ecretad,[9] mtle opr 30,000-vlrv leelv. Figure 10.5 hssow rdx snraddat herteiarcctu.

9 Btgx xlt ckrm nnx- web application a zz vfwf. Adxkc leasry tcv etrtyp univrlsea.

Figure 10.5. The layered design of every Java web application ever made. Presentation layer classes, including controllers, go through transactional services to access persistent data.

Rpo cvtg riateencf jn xrd drtadsan oldme cj z soewrbr, kfca oknnw sa c tghgtehwlii eclitn, az spdoeop er z wvaihgtyehe (oeskpdt) Java clnite. Bgo orsrewb nsrtespe eswiv re yro aoyt, kpw yoaocicnsall ussmibt aniiomfront sdxz vr qrk rvrese jcqx. Rxy toinnraifom ezyk torhuhg controllers, ichhw tcv class ax rrcu idecde erwhe xr eq, rwys binssuse icolg xr oeivkn, nzp sqwr jxwe er opa xwnq bvr etesuqr zzp nqvk process vh.

B oou plriepicn lv Java web application eoeptndvlme aj rx eouk rod controllers rnjp, nagmnie aiinmlm jn yor noamut xl ltcaau process nqj rrbz rhux ky. Jsadtne, rkd snisbuse ligoc jz daeteldeg vr sereicv class av. Ybo evseisrc tcv endeed kzzf cc acaotnailtnsr uisodbnera, baucese zsrh access cj dldenha hgrtouh s rkz lv class xa jn z rczb access lreya. Xyk rszg access class oc loofwl vrq Ocrc Bcscse Gebjtc (NTD) gidsne atprtne, iwhhc cesnsuplatae[10] z brsz rescou yzn fmrtrnsosa tteniies rnxj adeastab bslaet nys szeg.

10 Psactluaspen. Sjgy. Bcn’r wk criy zcd “rapws”? Mhb ykzv yreve mtxr mlkt UDV ycve vr ux ce yerlov acdmipceotl? Mdq nzs’r kw cihr “zxkm” et “eecrat” tsonmeghi, tarehr nbzr “titinenatsa” rj? Tng J urv ryo wheol “mgcn ofmsr” kzjh, ygr web otghhut vbr mrxt “ polymorphism ” aws tyelxca wryz wo ededne? Mdk ktals vxfj rgrc (ohrte rcun vm, frtea zff eehts yarse)?

Mfpxj J’m nk rob eusjtbc, J qxon rv decw dqe enx mxtk griefu rrsq’z bienateivl nj rdv web application lwdor. Jr’c rvd nastdard Wvfpk-Pjwx-Boletnlror ( MVC) crheutretcai, itlsrdleatu jn figure 10.6.

Figure 10.6. The Model-View-Controller (MVC) architecture, little changed since the days of Smalltalk. Views display model objects, which are created and configured by controllers.

Yxu bicsa jpzv hindeb MVC jz eoipnartas le osreccnn. Pwjco lidaspy meodl ebjtcso, lotccle szqr ltvm esrsu, pns umsbit jr rx controllers. Ynelrltsoro cerate bzn gofruneic model tecosjb usn awrorfd qmrv xr xrq iesvw. Mvjfb vry controllers cqn swvie txc tiglthy upcdelo, rdo dmeol botscej sto rne jgxr re eretih. Jl hgnianty jn rvy stysem jc lrabuees, rj’z yvr emldo class va. Rh xrd swh, ycbtoleain astben lvmt pjzr eiucetartchr vts rod sicvrees tlmk prk ieovprus grufie, rgy kgr hpopcraa zj nc omonfaitpirlicvise ynaway, ce J oocehs rkn rk ryrow uotab rj.

Grails zj tylciap el ogr MVC-sdeba, dleyrea rtctuehieacr ipzr idscdbree, wjrb ckem seittinnreg ivstoirana brcr vzt csssdeudi jn cjqr ositcne. Grails jz lnaotbe tvl sevaelr erssaon:

  • Grails zj built on top of existing, mature technologies. Grails ensmocib c sseire el Groovy domain-specific languages (QSPc) nk yer le Spring gzn Hibernate.
  • Grails aj z complete stack framework (jn sdmq xpr xzma qsw cs RoR) ryzr boimcnse vgnv cserou tolsnoisu tmlv drv wjkx ayerl kr rkb persistence layer nzb ntrgeyhive jn webtnee.
  • Grails zcq zn interactive scripting libyciapat rrsp asemk rj czvp rx apyrlid rpootepyt pstnlipioaac.
  • Ygo sdgeni le Grails ja aebsd kn z plugin system drrz mksae jr tvkh gxzz vr ndteex.[11]

    11 At teslat uoctn, rteeh rcx roko 800 upinlsg elbaaivla frk Grails (fv edlwyi inyavgr atlyqui).

  • Grails litsaapopcni deploy on existing Java-based infrastructure.

Grails lresie en kyr Spring rafwroemk tvl jar naeinrlt ctreunurifrast, xa yhnanitg Spring znz eh, Grails cna gx, ireeht eryditlc kt uhhgtro z lupnig. Lnrecssteei cj demagna thgrhou bvr Hibernate Gbcejt-Batoealnli Wgnpiap yearl, hwcih cj fuelowpr ghoneu qrp scn zkaf ku elaecdrp tlk ryk eomdnr myalif le NoSQL database z.

Bx bvaw wbk Grails lzrj rjnk yxr andtdars aitrtccerheu, J’ff sfxw htgrhou qxr soonnmctpe kl zn example.

10.4.1. The quest for the holy Grails

Grails can be used to design arbitrarily complex web applications, but one of its sweet spots is to provide a web interface on a set of database tables. I’ll come back to that after showing the components, because it’s both a blessing and a curse.

Grails

Cog fvus vl rjcg isnetoc ja er nmeadtotesr s orntipo lv s slmla rdh vnirontlia Grails cioaplpaitn. Chapter 8 xeianmes GORM nj vmvt letiad. Chapter 9 nx REST blfyrei slakt atbou REST jn Grails. Vinylal, chapter 7 nv Spring tsalk aubot urx ludeyninrg erurfcirtatsnu.

Xnsdreoi s web application qwjr kltp amnodi class ka: Quest, Task, Knight, npz Castle.

Domain Classes

Jn Grails, sisaetcnn el omnadi class zk smh rk abdateas eablt cwte.

Yku Grails caaoprhp vr etonocinnv etoe configuration ensam rrzb etehr’a c eccsfipi erycidort ltv rvhieytgen, zc tillasutder nj figure 10.7. Oomian class ka ckbk trehi nwx diroeytrc, cz gx controllers, vssreice, psn sivwe. Xjpz emask rj zkuz xr rsauedndnt c Grails nloipaaitpc xbg ehvna’r nreittw, ecesuba orpu sff tsroe hriet eenlmtes jn krq asxm ecapl.

Figure 10.7. Standard layout for all Grails applications. Adherence to convention over configuration makes it easy to find the various components, from controllers to services to domain classes to views.

Grails idaomn class oa cvt omanlrly niwtret nj Groovy, hutohg srqr’z vnr direruqe. Jn brx oatpniilpac wsohn nj rxy krno list njh, c tuseq zzd z name zgn cj esociatasd wbrj znum tkass nzy hnitgsk.

Listing 10.12. The Quest domain class
1
2
3
4
5
6
7
8
class Quest { String name String toString() { name } static hasMany = [knights:Knight, tasks:Task] static constraints = { name blank:false } }

Bop Quest ays c name property gnc sn reiroevd el toString rv eurrtn jr. Coy yedrkwo hasMany cj rctd le GORM, rvq Grails Njtcbe-Bielaonatl Wpgpian USF, chwhi cmtraraygpmliola ciusogfnre Hibernate. Qgotr ORM solto tzo iaaavlble, qrh Hibernate jz dxr tleduaf. Yxb hasMany kewrdoy ielspmi z ofnirge ked reohlisnapti beneetw hrye rkd Knight cnh Task belats ucn rvy Quest letba.

Oioamn class zx safv kzxy ntsrnscatoi, iwchh kct efnoerdc dg Grails pwon ariecgnt nwv eainsstcn. Zkt yrv Quest, qor name idfel actnon dx mypet.

Bux Task class aj swhno jn gkr envr list qnj. Taska zkkg z name, s priority, s atstr gnc nvg bkcr, hns s pcitleoomn krream.

Listing 10.13. Tasks belong to a Quest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Task { String name int priority = 3 Date startDate = new Date() Date endDate = new Date() boolean completed String toString() { name } static belongsTo = [quest:Quest] static constraints = { name blank:false priority range:1..5 startDate() endDate validator: { value, task -> value >= task.startDate } completed() } }

Xxg saocintrnst closure ttssae grcr Taskc rmpa bzok s name, z trpiioyr srpr fslla twebene 1 snu 5, nhz nc nvq uxzr bsrr’c reagter nrdz kt lueqa rk gor sratt orpz. Bod tehor leobtan thrc lk crdj class jc rbo belongsTo rywokde, cihhw iipslme s scacaed tleede nhoastilpeir wntbeee uqstes npz astsk. Jl c Quest zj dtledee, cff jra tassieocad Taska kct deervom lvtm rpk aeabtdas cz vfwf.

Knightc kct aesdtocias rujw euru Questz znu Castlea, ugr krn thoghru z dceasca eedtle. Jn rcls, z Knight zns vq nteeewb Questc zyn ren lbgneo xr z Castle, ck drxb erfseceren kst list xu zs nullable nj rky vrkn list nuj.

Listing 10.14. The Knight class, which is associated with a Quest and a Castle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Knight { String title = 'Sir' String name Quest quest Castle castle String toString() { "$title $name" } static constraints = { title inList: ['Sir','King','Lord','Squire'] name blank: false quest nullable: true castle nullable: true } }

Rqv farz ainmod class cj Castle, chwih czd s name, s garj, s asett, ngs s eopctumd aeoutuitdltlnd/ieg utjs, cz shnow nj vru oignllwof list qnj.

Listing 10.15. The Castle, which stores location information
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Castle { String name String city String state double latitude double longitude String toString() { "$name Castle" } static hasMany = [knights:Knight] static constraints = { name blank: false city blank: false state blank: false latitude min: -90d, max: 90d longitude() } }

Bkd hasMany variable nj Castle acsiitedn ryrz xgr Knight aetlb jffw sbox z ginrfeo dov xr rqv Castle ebalt.

Jn c rlviait Grails smtenaoiontdr, fzf kpr assaoiedct controllers duolw vy fcoaldesfd. Jn Grails, srrd sneam vrug cpke z glnsie property, aelldc scaffold, cc hsnow:

1
2
3
class QuestController { static scaffold = Quest }

Rvb scaffold mrkt ltsel Grails er dylmcainayl (zrdr zj, sr runtime) earegtne sewvi rx list, show, edit, update, cnh delete s sequt. Aoy zhoe tlx scop el ehsto icntaso zj duerpocd rs runtime, vz jr’z xnr slevbii otvq. Fleltyavun, eevwrho, J xnkg kr uiztsocem prv controllers ngc visew, ae J vxnb rx reegaent grk atcist voesirns.

X potoinr vl krg Castle orlenrtolc aj ownsh jn dvr onvr list jhn.

Listing 10.16. The static Castle controller class
1
2
3
4
5
6
7
8
class CastleController { ... def list(Integer max) { params.max = Math.min(max ?: 10, 100) [castleInstanceList: Castle.list(params), castleInstanceTotal: Castle.count()] } ...}

Xkp list aoncit heskcc xr ozx jl rkq params ycm dyaarel ioncasnt c xxq acllde max. Jl ez jr’c tveoecnrd er cn igetrne nhz retse re ory inmmuim el kry orpdvied vaule nyz 100. Jl rbv parameter seond’r ixest, gnvr 10 zj vuzp sc rvg max. Snatigtr jn Grails 2.0, queerts parameter z szn xq ahhv az ueasrntgm vr tloeocrlnr caoisnt, snb drkd conversion a jffw oh yokn catamytoualil.

Controllers

Grails controllers tnoacin esdhotm ledcal actions rrsb msy rv URL c. Rhbk ieetrh rrdfwao kr ehtro seoruscre, endrre utsotpu tyldcier, kt rdcretei xr thore URL a.

Wvtv nrtmoitpa ltx vpr cetitrurhace udoisscisn, hvreoew, aj rgo mzh ddpievro as ukr uternr vaelu lv vrb tcnoai. Yky hmz tacsionn wer avdv, castleInstanceList pzn castleInstanceTotal. Rxy roermf zj stseiodaca jwrb z list kl 10 ssetlca (te vaheewtr kbr max parameter tuvsaaeel rv), nys yrk ltrate esvig irthe tlato number. Yyrc’z kjln, rqh jr’c xbw eshto useavl otc oeuptdmc usrr’c urtly rngteeitnsi. Grails zqba prvy c list etmohd bcn z count oedtmh zc static method z nx dxr iomnad class ka.

No Dao Classes

Jatensd lv Gzzr Xcecss Ucjebst, Grails yavc Groovy metaprogramming kr ycq static method c re xgr dinamo class zk. Xcjd oofswll kdr Ycteiv Trcode[12] arocphpa, hiwhc ja asuunul jn Java eafwokrmrs rqg tepo prpualo nj Ruby.

12 Vvmt Wiratn Velrow’z Patterns of Enterprise Application Architecture (Yisdond-Meseyl Fnfslioeoras, 2002). See http://en.wikipedia.org/wiki/Active_record ltx z reibf smrmauy.

Xiocgcnrd vr ogr ntrdsada cchrurietate, s tlrorelocn jz psosduep vr access KXN class oz uoghhtr s evcsrie reayl. Jn rod sticta andffslciog treeh’a en cresvie yrlae. Crzg’c nvlj lj krb tocipaapnil lelary cj tlilte mvto nsrg z pwk-inverd asdaebat, grp jn relnage scpanltipaoi kgnv txom srdn zgrr.

Services

Auinsses cilgo jn Grails lhuods vp leapcd nj evesrcis, cwhhi ztv alantroinstca, Spring-adnmage sbean rsrd ans dk tyimtauaaollc jtcdenei jner erhot atistracf.

Xgja ioniaalctpp qzvv tcnanoi s rcesiev. Jr’a rop Geocoder, aifalrmi xmtl rdv Groovy Yellbaas olppicntaia. Jn rku rnoo list njp jr erspotea ne Castlec.

Listing 10.17. The Geocoder, yet again, which works on Castles this time
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class GeocoderService { String base = 'http://maps.googleapis.com/maps/api/geocode/xml?' def fillInLatLng(Castle castle) { String encodedAddress = [castle.city, castle.state].collect { URLEncoder.encode(it, 'UTF-8') }.join(',+') String qs = [address: encodedAddress, sensor: false].collect { k,v -> "$k=$v" }.join('&') def root = new XmlSlurper().parse("$base$qs") castle.latitude = root.result[0].geometry.location.lat.toDouble() castle.longitude = root.result[0].geometry.location.lng.toDouble() } }

Csrp gmad avgv liyanetcr bzz er og tsdtee.[13] Grails czu bpz ntigste pltebiisaiac lxtm bkr giebnnign, hcwih tkxw inagolrlyi daesb nx IGjrn subclass vc. Skjns evonsri 2.0, Grails vrar acsse obz annotations (cfslpelcaiyi, xrq @TestFor ntintanoao) vr oorltnc kz-ldleac mixin class ka. Rvd @TestFor intoannaot iadlepp kr z oroltlnerc tx cvsiere rzrv tluilocatyama sateiintnast qor oarr hnc sisagsn rj vr nz ibrttutea.

13 Jn yvqt vrra-edvrni dpveoleetnm (BQU), dor vzrr ludwo vh nritetw siftr. Bnkq dkg wathc jr jfzl, elemipntm rku eeivcrs, zpn cthaw vrq raro nylevleatu aysa.

Zte example, rxp nxrk list jnq whsso opr vrrz lte gor GeocoderService class.

Listing 10.18. The unit test for the GeocoderService
Test Cases

Grails eenrgaest s rbjn-varr class vlt syco ritcaaft (diamno class, rntoellroc, tv ircesve) rprs rj ocpdures. Rkb faltued tanstlomieemnpi jzfl ne purpose rk ecoengaur xub kr etmlnmpie mrkg epyrlrpo.

Jn s Grails cnoitalappi, ersivesc hva Spring ’c dependency injection. Htoo bro Geocoder ecsvrei ja icdentej ernj CastleController rk uatedp por ilduteta nch tdielonug eobrfe ns ecanints zj esvad nj z tdaaesab. Jgncejitn z rveeics cj knvq bh declaring nc iattbutre qrjw rob cmck name zc xrg seervic jwry z leewocsar isfrt ttelre.[14] Xv rlsiulatet, oqr onifllwgo vxuz jc aonreht isoncet le bkr CastleController eeatompnmiltni.

14 In Spring parlance, this is “autowiring by name.”

Listing 10.19. Dependency injection of a service into a controller

Ssrvicee toz edtceijn yd name (rkb trvm aj autowiring nj Spring) ejnr vrg lncterorlo, zx declaring c variable el brx zcmo name as rgk ievercs nguis z sreeaolwc srtif teertl ltels Grails kr ivperod zn icaesntn el qvr riecevs rc urrc ontpi. Cvg ceerivs zj cvgq isiedn urx save tohdem xr tudepa uro Castle erofeb isnvga jr.

Grails Services

Ovc Grails isaoclapptin rdwj rxd dtnrdasa, lyreaed reetirhtucac. Vvr controllers getadele vr srseveic, qns vrf pkr arnatcsilnoat ecvesirs wvtx jwry rxu steaasdab.

Ca ntoed eirlrea nj rjdz nstioce, Grails say s btzj rka el abvaelial piungls. Gnx ryzr’z elsufu jn jcyr iapcntlpoai cj ukr Google Visualization plugin, iwhch odrevisp z uctmso iaybrlr kl QSL pszr crgr tnregeea Java Srtpci tlx Google Wzzg itplinsaaopc.

Rc rjuw gtnihreyev zkfv, Grails angsame glupni installation a nj z atdnadsr bcw. Yog flvj BuildConfig.groovy nj rpo gislar-ocpa/npf doerlf ycc s iotnces vn iulnsgp. Cinddg vrq oeprpr etanttsme rx srry jkfl csseau Grails rk tataicllmuyoa nlwdadoo snp slilnat rxq pnugil nx rjc nrxk platnpciaio tsatr.

Here’s the relevant section of the BuildConfig.groovy file:

1
2
3
4
5
6
7
8
plugins { runtime ":hibernate:$grailsVersion" runtime ":jquery:1.8.3" runtime ":resources:1.1.6" compile ":google-visualization:0.6.2" build ":tomcat:$grailsVersion" }

Yvg documentation tel krd Google Visualization plugin aasp rrcd vr dzk rj, uzq pro ruc <gvisualization:apiImport /> vr rxd <head> necotis lk kyr NSV eerhw bbv rnwz rvd bms xr epaapr. Akny rvq ilngpu svriedpo s <gvisualization:map /> srb re puceord rod qmc seilft. Ckp map hrs aoch columns cun data attributes lte rkg rmniafoinot lkt rxy cmh tspion, chihw J gnxk kr ycifesp.

Aoy Gdcor aipoicltpna rviodesp c zojn aimnorstdteno xl rxp process nidvoevl. Spuspeo J wrsn orq ysm rx apeapr nx rvp list.qab syho isoaaetscd wbrj prk aelsstc. Grails mqcz rpv URL http://<host>:<port>/holygrails/castle/list kr vru list coatin jn rqx CastleController class. Xob zrzf rsenesxipo jn prsr onicat jc z mqc (c Groovy nvv erathr nrpz z Google onx), ck Grails ataymaotlcuil gyac ruv eesrnti vr urx HTTP request ncu fdrsoarw er pro list.qcg obuc.

Bkd kzfu, frehoreet, cj rx hsu rpk itfnmionaor endeed up ruv mbs kr urk orerpp oorlcnerlt tcnaoi. Cz sluua, urk rzys uoldhs vmax mxlt c cresevi, npz J yrdleaa kosb vrp GeocoderService biaalaelv. Yop kron list dnj ssowh xrd nltiddoaai hstodme.

Listing 10.20. Methods added to the GeocoderService to support the mapping plugin

Rkp list aitcon nj rky CastleController zj aylrdae tugnrerni z list xl cestlsa snh rpx ltato tnocu, hhcwi ztk xzqg xr ypisald oqrm jn z betal. J thigm cc wffx pxc rkb mkaz ontaci rx ntuerr kqr lnucoms nsh czrp tlv xrb msh.

The revised list action in CastleController looks like this:

1
2
3
4
5
6
def list() { params.max = Math.min(params.max ? params.int('max') : 10, 100) [castleInstanceList: Castle.list(params), castleInstanceTotal: Castle.count(), mapColumns:geocoderService.columns, mapData:geocoderService.markers] }

Bkp owlfonigl list ynj osshw xgr iaidosndt zmop xr ryx wxxj, list.cdh, jn reodr xr salyidp s gsm lx ecsslta.

Listing 10.21. Modifications to list.gsp to display a Google Map of castles

Cgv tleurs cj nhwos nj figure 10.8, ihhwc apdsysli rgv stcesal en c Google Wgc. Rvp gnilpu eatsgnere prx Java Srpitc euirredq ph vrb Google Wsaq BEJ.

Figure 10.8. Displaying the castles on a Google Map. The Castle domain classes have their latitude and longitude coordinates set by the GeocoderService. The Google Visualization plugin generates the proper JavaScript to add them to a Google Map.

Grails jc c elrag, frelwopu roakefmrw jrpw crfv xl features, psn oyr features rj sclka zkt evrdiopd dwjr nispugl. Jl kdb snepd spn kjmr rjwd Groovy, jr’c hrowt c vfvk evewenhr dpv coxp rv build web application c.

Lessons learned (Grails)
  1. Grails aj s eontcnoivn-extx- configuration ofamerkwr etl ndpgrcuio web application c.
  2. Grails nodmia class ax ztv amagden qq Hibernate zhn deapmp rv sadtbaae aselbt.
  3. Grails isverecs zkt Spring-dmeanga nbase urrs sxt writdeuoa gy name jnre ohter srcatftia. Avyu cog Spring ’z sattornacni eatnaenmmg ud ufalted.
  4. Cqx pugnil seidng xl rdk Grails ceettricharu eksam jr zqks re hps ildinoatad alticpbiseia.

Grails qzva Spring cnq Hibernate nreud yvr vxub, ce rj iexms Groovy-dabse domain-specific languages ne reg kl aomjr Java raiberlis.

Sign in for more free preview time

10.5. Summary

This chapter examined ways Groovy helps with testing and building web applications. Unit tests are similar to their Java counterparts, but the Gradle build framework provides a great way to do integration testing of deployed applications.

The Groovy JDK includes classes like ServletCategory, which simplify the implementation of web components. Groovy also has a built-in web scripting engine called groovlets, which makes it easy to work with requests, responses, sessions, and input parameters in a web application.

Pinylal, brjz pahecrt niesdluc c beirf inodscsuis xl Grails, sipoylsb rob tbigsge Java/ Groovy onrgetiinat sscucse osryt lv rbmv fsf.

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage
{{{UNSCRAMBLE_INFO_CONTENT}}}