434

Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About
Page 2: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BuildingApplicationswithScala

Page 3: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TableofContents

BuildingApplicationswithScalaCreditsAbouttheAuthorAcknowledgmentsAbouttheReviewerwww.PacktPub.com

Whysubscribe?Preface

WhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeDownloadingthecolorimagesofthisbookErrataPiracyQuestions

1.IntroductiontoFP,Reactive,andScalaFunctionalprogrammingPrinciplesoffunctionalprogramming

ImmutabilityDisciplinedstatePurefunctionsandnosideeffectsFirst-classfunctionsandhigher-orderfunctionsTypesystemsReferentialtransparency

InstallingJava8andScala2.11ReadEvalPrintandLoop-REPLScalaHelloWorldusingtheREPL

ScalaREPLHelloWorldprogramScalaobject-orientedHelloWorldprogramScalaHelloWorldAppintheScalaREPLJavaHelloWorldapplication

Scalalanguage-thebasicsScalavariables-varandval

ScalaREPLvarusageScalavalusageattheScalaREPL

CreatingimmutablevariablesScalavariabletypeintheScalaREPL

Page 4: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalavariableswithexplicittypingattheScalaREPLScalaconditionalandloopsstatements

IfstatementsinScalaREPLIfstatementsinreturnstatementsinScalaREPLBasicforloopinScalaREPLForwithListinScalaREPLForwithifstatementsforfiltering-ScalaREPLJavacodeforfilteringevennumbers

ForcomprehensionsForcomprehensioninScalaREPLJavacodeforperformingfilteringwithcollections

ScalacollectionsCreating,removing,andgettinganitemfromamutablelistinScalaREPLScalatuplesScalaimmutableMapinScalaREPLScalamutableMapsatScalaREPL

MonadsScalaMapfunctioninScalaREPLOptionMonadinScalaAlistofallmethodsusingtheScalaREPL

Scalaclass,traits,andOOprogrammingAsimpleScalaclassinScalaREPLScalaplainoldJavaobjectinScalaREPLPersonclassinJava

TraitsandinheritanceScalainheritancecodeinScalaREPLScalatraitssamplecodeinScalaREPLScalatraitsusingvariablemixingtechniqueatScalaREPLScalatypealiassampleinScalaREPL

CaseclassesScalacaseclassesfeatureinScalaREPL

PatternMatcherSimplePatternMatcherinScalaAdvancedpatternmatcherinScalaREPLAdvancedcomplexpatternmatcherinScalaREPL

PartialfunctionsSimplePartialfunctioninScalaREPLScalaPartialFunctionwithoutOOusingcasestatementsinScalaREPLPartialFunctioncompositioninScalaREPL

Packageobjectspackage.scalaMainApp.scala

FunctionsPartialapplication

Page 5: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PartialfunctioninScalaREPLCurriedfunctions

Curriedfunctions-ScalaREPLCurriedtransformationinScalaREPL

OperatoroverloadingScalaoperatoroverloadinginScalaREPL

ImplicitsScalaImplicitsinSCALAREPLImplicitParameteratScalaREPL

FuturesSimpleFuturecodeinScalaREPLAcompleteFuturesampleatScalaREPL

ReactiveProgramingandRxScalaSimpleObservablesScalawithRxScalaSimpleObservablesScalawithRxScala-ExecutionintheconsoleComplexScalawithRxScalaObservables

Summary2.CreatingYourAppArchitectureandBootstrappingwithSBT

IntroducingSBTInstallingSBTonUbuntuLinuxGettingstartedwithSBTAddingdependenciesGeneratingEclipseprojectfilesfromSBTApplicationdistributionHelloworldSBT/ScalaAppBootstrappingourPlayframeworkappwithActivatorActivatorshellActivator-compiling,testing,andrunningSummary

3.DevelopingtheUIwithPlayFrameworkGettingstartedCreatingourmodelsCreatingroutesCreatingourcontrollersWorkingwithservicesConfiguringtheGuicemoduleWorkingwithviews(UI)Summary

4.DevelopingReactiveBackingServicesGettingstartedwithreactiveprogramming

IPriceService-ScalatraitPriceService-RxScalaPriceServiceimplementationGuiceInjection-Module.scalaNGServiceEndpoint

Page 6: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PlayframeworkandhighCPUusageRndDoubleGeneratorControllerIRndService.scala-ScalatraitRndService.scala-RndServiceimplementationModule.scala-GuiceInjectionsmain.scala.htmlproduct_details.scala.html

Summary5.TestingYourApplication

UnittestingprinciplesMakingcodetestableIsolationandself-containedtestsEffectivenamingLevelsoftesting

TestingwithJunitBehavior-DrivenDevelopment-BDDMyFirstPlaySpec.scala-FirstBDDwithScalaTestandthePlayframeworkTestingwithPlayframeworksupport

ProductService.scala-FIXthecodeissueImageServiceTestSpec.scala-ImageServiceTestReviewServiceTestSpec.scala-ReviewServicetest

TestingroutesRoutesTestingSpec.scala-Playframeworkroutetesting

ControllertestingRndDoubleGeneratorControllerTestSpec.scala-RndDoubleGeneratorControllertestsIntegrationSpec.scalaProductControllerTestSpec.scalaproduct_index.scala.htmlImageControllerTestSpec.scalaimage_index.scala.htmlReviewControllerTestSpec.scalareview_index.scala.htmlApplicationSpec.scalaNGServiceImplTestSpec.scalaNGServiceEndpointControllerTest.scala

Summary6.PersistencewithSlick

IntroducingtheSlickframeworkMySQLsetupConfiguringSlickinourPlayframeworkapp

ConfigurethedatabaseconnectionFPMMapping

ProductDaoReviewDAO

Page 7: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImageDaoSlickevolutionsRefactoringservicesRefactoringcontrollersConfiguringDAOpackagesinGuiceRefactoringtestsGenericmocksServicetestsControllertestsRunningtheapplicationSummary

7.CreatingReportsIntroducingJasperReportsJasperReportsworkflowJaspersessionsInstallingJaspersoftStudio6ConfiguringMySQLDataAdapterinJaspersoftStudioCreatingaproductreportCreatingareviewreportCreatinganimagereportIntegratingJasperReportswithPlayframework

build.sbtGenericreportbuilderAddingthereporttotheproductcontrollerAddingthereporttothereviewcontrollerAddingthereporttotheimagecontroller

Routes-addingnewreportroutesNewcentralizedreportsUI

AddingthereportbuttonforeachviewSummary

8.DevelopingaChatwithAkkaAddingthenewUIintroductiontoAkkaIntroductiontotheActormodel

WhatisanActor?MessageexchangeandmailboxesCodingactorswithAkkaActorroutingPersistenceCreatingourchatapplicationThechatprotocolThechatcontroller

ImplementingthechatcontrollerConfiguringtheroutesWorkingontheUI

Page 8: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingAkkatestsScalatestforAkkaActorChatroomActortestChatBotAdminActortest

Summary9.DesignYourRESTAPI

IntroductiontoRESTRESTAPIdesign

HTTPverbsdesignUniformAPIResponsewithHTTPstatuscodesRESTAPIpatternsAPIversioningSomeanti-patternstobeavoided

CreatingourAPIwithRESTandJSONRestApiContoller

RESTAPIFrontControllerimplementationJSONmappingConfiguringnewroutesTestingtheAPIusingthebrowser

CreatingaScalaclientConfiguringplugins.sbtConfiguringbuild.sbtScalaclientcode

CreatingourRESTclientproxiesCreatingScalaTesttestsfortheproxiesAddingbackpressure

TheleakybucketalgorithmScalaleakybucketimplementationTestingbackpressure

AddingSwaggersupportSwaggerUI

BuildandinstallSwaggerStandaloneSummary

10.ScalingupStandalonedeployReportsfolderChangingreportbuilderDefiningthesecretRunningthestandalonedeployArchitectureprinciples

Serviceorientation(SOA/microservices)PerformanceScalability/Resiliency

Page 9: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalabilityprinciplesVerticalandhorizontalscaling(upandout)CachingLoadbalancerThrottlingDatabaseclusterCloudcomputing/containersAutoScalingAnoteaboutautomationDon'tforgetabouttelemetryReactiveDriversanddiscoverability

Mid-Tierloadbalancer,timeouts,Backpressure,andcachingScalingupmicroserviceswithanAkkaclusterScalinguptheinfrastructurewithDockerandAWScloudSummary

Page 10: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BuildingApplicationswithScala

Page 11: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BuildingApplicationswithScalaCopyright©2016PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:December2016

Productionreference:1021216

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

Birmingham

B32PB,UK.

ISBN978-1-78646-148-3

www.packtpub.com

Page 12: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Credits

Author

DiegoPacheco

CopyEditor

SoniaMathur

Reviewer

YuanhangWang

ProjectCoordinator

SuzanneCoutinho

CommissioningEditor

KunalParikh

Proofreader

SafisEditing

AcquisitionEditor

DenimPinto

Indexer

TejalDaruwaleSoni

ContentDevelopmentEditor

RohitSingh

Graphics

JasonMonteiro

TechnicalEditor

JijoMaliyekal

ProductionCoordinator

MelwynDsa

Page 13: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AbouttheAuthorDiegoPachecoisanexperiencedsoftwarearchitectandDevOpspractitionerwithover10yearsofsolidexperience.HehasledarchitectureteamsusingopensourcesolutionssuchasJava,Scala,AmazonWebServices(AWS),Akka,ApacheCassandra,Redis,ActiveMQ,NetflixOSSStack-SimianArmy,RxJava,Karyon,Eureka,andRibbononcigcustomersinBrazil,London,Barcelona,India,andtheUSA.Diegohasapassionforfunctionalprogrammingandiscurrentlyworkingasasoftwarearchitect/agilecoachwithScala,Akka,andNetflixOSS.Duringhisfreetime,heenjoysgaming,blogging,andplayingwickedtunesonhisguitar.Youcancheckouthisblogathttp://diego-pacheco.blogspot.in/.

Someofhiscoreskillsincludethefollowing:

ArchitecturedesignandarchitecturecodingforhighscalablesystemsDistributedsystemsusingSOAandmicroservicesprinciples,tools,andtechniquesPerformancetuningandDevOpsengineeringFunctionalprogrammingandScalaAgilecoachingandservantleadershipforarchitectureteamsConsultancyondevelopmentpracticeswithXP/Kanban

Moreabouthimcanbefoundatthefollowing:

Linkedin:https://www.linkedin.com/in/diegopachecorsBlog:http://diego-pacheco.blogspot.in/Github:https://github.com/diegopachecoSlideshare:http://www.slideshare.net/diego.pacheco/presentationsPresentations:https://gist.github.com/diegopacheco/ad3e3804a5071ef219d1

HisrecentlecturesincludeNetflix(https://www.youtube.com/watch?v=Z4_rzsZd70o&feature=youtu.be),QCon(http://qconsp.com/sp2016/speaker/diego-pacheco),andAmazon(http://www.meetup.com/Sao-Paulo-Amazon-Web-Services-AWS-Meetup/events/229283010/).

Page 14: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AcknowledgmentsFirstofall,I'mverythankfulforeverythingGodhasgiventomeinlife.So,IneedtosaythankyoutoGodatleastthreetimes.ThankyouGod,thankyouGod,thankyouGod.I’mverygladtohavefinishedthisbook,andIalsoneedtosayabigthankyoutoallmyfamilyandsupportivefriends,especiallyAndressaBicca,mytruelove;mymother,DeniseMaris;mygrandmother,Walkyria;andmydearfriends,MargaridaAvila,AdãoAvila,IsraelPrestes,andTaisdaRosa,foralltheirloveandsupport.IneedtosaythankstoPackt,especiallytoKirkD'costaandRohitKumarSinghforbeinggreateditors.Also,Ineedtosaythankyoutoilegra.comandespeciallytoIvãBoesingandRomuloDornellesforallthespace,trust,andsupport.Also,Icannotforgettosaythankyoutomycoworkers,customers,andfriends,whoaregreatpeopletoworkwith,andwhoI’velearnedalotfrom:SamSgro,DanielWildt,AnibalRojas,AlexandrePoletto,JefersonMachado,NilseuPadilha,JacksonSantos,ChristopheMarchal,JoelCorrea,andRafaelSouza.Finally,thankyoutoallofyouwhoboughtthisbookandhavereadit--youareawesome!

Page 15: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AbouttheReviewerYuanhangWangdescribeshimselfasanenthusiastofpurelyfunctionalprogrammingandneuralnetworks,withaprimaryfocusonDomainSpecificLanguage(DSL)design,andhehasdabbledinseveralfunctionalprogramminglanguages.HeiscurrentlyadatascientistatChinaMobileResearchCenter,workingonatypeddataprocessingengineandoptimizerbuiltontopofseveralbigdataplatforms.

Page 16: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

www.PacktPub.comForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusatservice@packtpub.comformoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www.packtpub.com/mapt

Getthemostin-demandsoftwareskillswithMapt.MaptgivesyoufullaccesstoallPacktbooksandvideocourses,aswellasindustry-leadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.

Page 17: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 18: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PrefaceFunctionalprogrammingstartedinacademiaandendedupintheITindustry.TheScalalanguageisamulti-paradigmlanguageusedbybigplayersandlargeorganizationsthathelpsyougetthecorrect(inthesenseofpurefunctionalprogramming)softwareand,atthesametime,softwarethatispracticalandscalable.

Scalahasaveryrichecosystem,includingPlayFramework,Akka,Slick,Gatling,Finable,andmore.Inthisbook,wewillstartrightfromthebasicprinciplesandideasonfunctionalandReactiveXprogramming,andthroughpracticalexamples,youwilllearnhowtocodewiththemostimportantframeworksoftheScalaecosystem,suchasPlay,Akka,andSlick.

YouwilllearnhowtobootstrapaScalaapplicationwithSBTandActivator,howtobuildaPlayandAkkaapplicationstepbystep,andwecoverthetheoryofhowtoscalemassiveScalaapplicationswithcloudandtheNetflixOSSstack.ThisbookwillhelpyoutogofromthebasicsubjectstothemostadvancedonesinordertomakeyouaScalaexpert.

Page 19: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WhatthisbookcoversChapter1,IntroductiontoFP,Reactive,andScala,looksathowtosetupaScaladevelopmentenvironment,thedifferencebetweenfunctionalprogrammingandobject-orientedprogramming,andtheconceptsoffunctionalprogramming.

Chapter2,CreatingYourAppArchitectureandBootstrappingwithSBT,discussestheoverallarchitecture,SBTbasics,andhowtocreateyourownapplication.

Chapter3,DevelopingtheUIwithPlayFramework,coverstheprinciplesofwebdevelopmentinScala,creatingourmodels,creatingourviews,andaddingvalidations.

Chapter4,DevelopingReactiveBackingServices,introducesyoutoreactiveprogrammingprinciples,refactoringourcontrollers,andaddingRxScalatoourservices.

Chapter5,TestingYourApplication,looksintotestingprincipleswithScalaandJUnit,behavior-drivendevelopmentprinciples,usingScalaTestspecsandDSLinourtests,andrunningourtestswithSBT.

Chapter6,PersistencewithSlick,coversprinciplesofdatabasepersistencewithSlick,workingwithFunctionalRelationalMappinginyourapplication,creatingthequeriesyouneedwithSQLsupport,andimprovingthecodewithasyncdatabaseoperations.

Chapter7,CreatingReports,helpsyouunderstandJasperreportsandadddatabasereportstoyourapplication.

Chapter8,DevelopingaChatwithAkka,discussestheactormodel,actorsystems,actorrouting,anddispatchers.

Chapter9,DesignYourRESTAPI,looksintoRESTandAPIdesign,creatingourAPIwithRESTandJSON,addingvalidations,addingbackpressure,andcreatingaScalaclient.

Chapter10,ScalingUp,touchesuponthearchitectureprinciplesandscalinguptheUI,reactivedrivers,anddiscoverability.Italsocoversmiddle-tierloadbalancers,timeouts,backpressure,andcaching,andguidesyouthroughscalingupmicroserviceswithanAkkaclusterandscalinguptheinfrastructurewithDockerandAWScloud.

Page 20: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WhatyouneedforthisbookForthisbook,youwillneedthefollowing:

UbuntuLinux14orsuperiorJava8update48orsuperiorScala2.11.7TypesafeActivator1.3.9JasperReportsDesignerWindowsfontsforLinuxEclipseIDE

Page 21: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WhothisbookisforThisbookisforprofessionalswhowantlearnScala,aswellasfunctionalandreactivetechniques.Thisbookismainlyfocusedonsoftwaredevelopers,engineers,andarchitects.Thisisapracticalbookwithpracticalcode;however,wealsohavetheoryaboutfunctionalandreactiveprogramming.

Page 22: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"ThenextstepistocreatetheenvironmentvariablecalledSCALA_HOME,andtoputtheScalabinariesinthePATHvariable."

Ablockofcodeissetasfollows:

packagescalabook.javacode.chap1;

publicclassHelloWorld{

publicstaticvoidmain(Stringargs[]){

System.out.println("HellowWorld");

}

}

Anycommand-lineinputoroutputiswrittenasfollows:

exportJAVA_HOME=~/jdk1.8.0_77

exportPATH=$PATH:$JAVA_HOME/bin

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"TheActorbehavioristhecodeyouwillhaveinsideyourActor."

Note

Warningsorimportantnotesappearinaboxlikethis.

Tip

Tipsandtricksappearlikethis.

Page 23: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook--whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

Tosendusgeneralfeedback,[email protected],andmentionthebook'stitleinthesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

Page 24: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 25: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Youcandownloadthecodefilesbyfollowingthesesteps:

1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou'relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Building-Applications-with-Scala.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

Page 26: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

DownloadingthecolorimagesofthisbookWealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesintheoutput.Youcandownloadthisfilefromhttps://www.packtpub.com/sites/default/files/downloads/BuildingApplicationswithScala_ColorImages.pdf

Page 27: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks--maybeamistakeinthetextorthecode--wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.

Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.

Page 28: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

Page 29: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,[email protected],andwewilldoourbesttoaddresstheproblem.

Page 30: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter1.IntroductiontoFP,Reactive,andScalaInourfirstchapter,wewilllearnthebasicconceptsofFunctionalPrograming(FP),reactiveprogramming,andtheScalalanguage.Theseconceptsarelistedasfollows:

SettingupaScaladevelopmentenvironmentwithEclipseScalaIDE.Basicconstructsofthelanguagelikevar,val,for,if,switch,andoperatoroverload.ThedifferencebetweenFPandobject-orientedprogramming.PrinciplesofpureFP:immutability,nosideeffects,statediscipline,composition,andhigherorderfunctions.ConceptsofFPsuchaslambda,recursion,forcomprehensions,partialfunctions,Monads,currying,andfunctions.PatternMatcher,recursion,reflection,packageobjects,andconcurrency.

Let'sgetgoing!

Page 31: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

FunctionalprogrammingFPisnotnewatall.TheveryfirstimplementationofFPisLispandisdatedfromthe1950s.Currently,wearelivinginapost-functionalprogrammingera,wherewehavethestrongmathprinciplesandideasfromthe50smixedwiththemostmodernandbeautifulpieceofengineering,alsoknowastheJavaVirtualMachine(JVM).Scalaisapost-functionalprogramminglanguagebuiltontopoftheJVM.BeingontopoftheJVMgivesusalotofbenefitssuchasthefollowing:

Scalaisapost-functionalprogramminglanguagebuiltontopoftheJVM.BeingontopoftheJVMgivesusalotofbenefitssuchasthefollowing:

Reliabilityandperformance:Javaisusedby10outof10topwebsiteswehavecurrently,likeNetflix,Apple,Uber,Twitter,Yahoo,eBay,Yelp,LinkedIn,Google,Amazon,andmanyothers.JVMisthebestsolutionatscaleandisbattle-testedbytheseweb-scalecompanies.NativeJVMeco-system:FullaccesstoalloftheJavaecosystemincludingframeworks,libraries,servers,andtools.Operationsleverage:YouroperationteamcanrunScalainthesamewaytheyrunJava.Legacycodeleverage:ScalaallowsyoutoeasilyintegrateScalacodewithJavacode.ThisfeatureisgreatbecauseitenablesJavalegacysystemintegrationinsidethebox.Javainteroperability:AcodewritteninScalacanbeaccessedinJava.

Scalawascreatedin2001atEPFLbyMartinOdersky.Scalaisastrongstatic-typedlanguage,andwasinspiredbyanotherfunctionallanguagecalledHaskell.ScalaaddressesseveralcriticismsoftheJavalanguage,anddeliversabetterdeveloperexperiencethroughlesscodeandmoreconciseprograms,withoutlosingperformance.

ScalaandJavasharethesameinfrastructureastheJVM,butintermsofdesign,ScalaisadifferentlanguageincomparisonwithJava.Javaisanimperativeobject-orientedlanguageandScalaisapost-functional,multiparadigmprograminglanguage.FPworkswithdifferentprinciplesthanobject-orientedprograming(OOP).OOPgotverypopularandwellestablishedinenterprisethankstolanguageslikeJava,C#,Ruby,andPython.However,languageslikeScala,Clojure,F#,andSwiftaregainingahugemomentum,andFPhasgrownalotinthelast10years.Mostofthenewlanguagesarepurefunctional,post-functional,orhybrid(likeJava8).Inthisbook,youwillseeScalacodecomparedwithJavacodesoyoucanseebyyourselfhowScalaiswaymorecompact,objective,anddirectthanJavaandimperativeOOPlanguages.

FPstartedatacademiaandspreadtotheworld;FPiseverywhere.BigDataandStreamprocessingsolutionslikeHadoopandSpark(builtontopofScalaandAkka)arebuiltontopofFPideasandprinciples.FPspreadtoUIwithRxJavaScript-youcanevenfindFPinadatabasewithDatomic(Clojure).LanguageslikeClojureandScalamadeFPmorepracticalandattractivetoenterpriseandprofessionaldevelopers.Inthisbook,wewillbeexploring

Page 32: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

bothprinciplesandpracticalaspectsoftheScalalanguage.

Page 33: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PrinciplesoffunctionalprogrammingFPisawayofthinking,aspecificstyleofconstructingandbuildingprograms.HavinganFPlanguagehelpsalotintermsofsyntax,butattheendoftheday,it'sallaboutideasanddevelopermindset.FPfavorsdisciplinedstatemanagementandimmutabilityinadeclarativeprogrammingwayratherthantheimperativeprogrammingmostlyusedbyOOPlanguagessuchasJava,Python,andRuby.

FPhasrootsinmathbacktoLambdacalculus-aformalsystemdevelopedinthe1930s.Lambdacalculusisamathematicalabstractionandnotaprogramminglanguage,butitiseasytoseeitsconceptsinprogramminglanguagesnowadays.

Imperativeprogrammingusesstatementstochangetheprogramstate.Inotherwords,thismeansyougivecommandstotheprogramtoperformactions.Thiswayofthinkingdescribesasequenceofstepsonhowtheprogramneedstooperate.WhatyouneedtokeepinmindisthekindofstylefocusonhowFPworksinadifferentway,focusingonwhattheprogramshouldaccomplishwithouttellingtheprogramhowtodoit.WhenyouarecodinginFP,youtendtousefewervariables,forloops,andIFS,andwritemorefunctionsandmakefunctioncomposition.

ThefollowingaretheCOREprinciplesofFP:

ImmutabilityDisciplinedstatePurefunctionsandnosideeffects/disciplinedstatesFirstclassfunctionsandhighorderfunctionsTypesystemsReferentialtransparency

Let'sunderstandtheseprinciplesindetail.

Page 34: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImmutabilityTheconceptofimmutabilityistheCOREofFP,anditmeansthatonceyouassignavaluetosomething,thatvaluewon'tchange.Thisisveryimportant,becauseiteliminatessideeffects(anythingoutsideofthelocalfunctionscope),forinstance,changingothervariablesoutsidethefunction.Immutabilitymakesiteasiertoreadcode,becauseyouknowthefunctionthatyouareusingisapurefunction.Sinceyourfunctionhasadisciplinedstateanddoesnotchangeothervariablesoutsideofthefunction,youdon'tneedtolookatthecodeoutsidethefunctiondefinition.Thissoundslikeyou'renotworkingwithstateatall,sohowwoulditbepossibletowriteprofessionalapplicationsthisway?Well,youwillchangestatebutinaverydisciplinedway.Youwillcreateanotherinstanceoranotherpointertothatinstance,butyouwon'tchangethatvariable'svalue.Havingimmutabilityisthekeytohavingbetter,faster,andmorecorrectprograms,becauseyoudon'tneedtouselocksandyourcodeisparallelbynature.

Page 35: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

DisciplinedstateSharedmutablestateisevil,becauseitismuchhardertoscaleandtorunitconcurrently.Whatissharedmutablestate?Asimplewaytoseeitisasaglobalvariablethatallyourfunctionshaveaccessto.Whyisthisbad?Firstofall,becauseitishardtokeepthisstatecorrectsincetherearemanyfunctionsthathavedirectaccesstothisstate.Second,ifyouareperformingrefactoring,thiskindofcodeisoftenthehardesttorefactoraswell.It'salsohardtoreadthiscode.Thisisbecauseyoucannevertrustthelocalmethod,sinceyourlocalmethodisjustonepartoftheprogram.Andwithmutablestate,youneedtolookupforallthefunctionsthatusethatvariable,inordertounderstandthelogic.It'shardtodebugfortheverysamereason.WhenyouarecodingwithFPprinciplesinmind,youavoid,asmuchaspossible,havingasharedmutablestate.Ofcourseyoucanhavestate,butyoushouldkeepitlocal,whichmeansinsideyourfunction.Thisisthestatediscipline:youusestate,butinaverydisciplinedway.Thisissimple,butitcouldbehardespeciallyifyouareaprofessionaldeveloper,becausethisaspectisnowusualtoseeinenterpriselanguagessuchasJava,.NET,Ruby,andPython.

Page 36: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PurefunctionsandnosideeffectsPurefunctionsaretheoneswithnosideeffects.Sideeffectsarebad,becausetheyareunpredictableandmakeyoursoftwarehardtotest.Let'ssayyouhaveamethodthatreceivesnoparametersandreturnsnothing--thisisoneoftheworstthingswecouldhave,becausehowdoyoutestit?Howcanyoureusethiscode?Thisisnotwhatwecallapurefunction.Whatarethepossiblesideeffects?Databasecall,globalvariables,IOcall,andsoon.Thismakessense,butyoucannothaveaprogramwithjustpurefunctions,becauseitwon'tbepractical.

Page 37: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

First-classfunctionsandhigher-orderfunctionsFirst-classmeansthatthelanguagetreatsfunctionsasfirst-classcitizens.Inotherwords,itmeanshavinglanguagesupporttopassfunctionsasargumentstootherfunctionsandtoreturnvaluesasfunctions.First-classfunctionalsoimpliesthatthelanguageallowsyoutostorefunctionsasvariablesoranyotherdatastructure.

Higher-orderfunctionsarerelatedtoFirst-classfunctions,buttheyarenotthesamething.Higher-orderfunctionsoftenmeanslanguagesupportforpartialfunctionalapplicationandCurrying.Higher-orderfunctionsareamathematicalconceptwherefunctionsoperatewithotherfunctions.

Partialfunctionsarewhenyoucanfixavalue(argument)toaparticularfunction,whichyoumayormaynotchangelateron.Thisisgreatforfunctioncomposition.

Curryingisatechniquetotransformafunctionwithmultipleparametersinasequenceoffunctionswitheachfunctionhavingasingleargument.Scalalanguagedoesnotforcecurrying,however,languageslikeMLandHaskellalmostalwaysusethiskindoftechnique.

Page 38: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TypesystemsTypesystemisallaboutthecompiler.Theideaissimple:youcreateatypesystem,andbydoingso,youleveragethecompilertoavoidallkindsofmistakesanderrors.Thisisbecausethecompilerhelpsinmakingsurethatyouonlyhavetherighttypesasarguments,turnstatements,functioncomposition,andsoon.Thecompilerwillnotallowyoudomakeanybasicmistakes.ScalaandHaskellareexamplesoflanguagesthatareStrong-type.Meanwhile,CommonLisp,Scheme,andClojurearedynamiclanguagesthatmayacceptwrongvaluesduringcompilationtime.Oneofthebiggestbenefitsofthestrongtypesystemisthatyouhavetowritefewertests,becausethecompilerwilltakecareofseveralissuesforyou.Forinstance,ifyouhaveafunctionthatreceivesastring,itcouldbedangerous,becauseyoucanpassprettymuchanythinginastring.However,ifyouhaveafunctionthatreceivesatypecalledsalesman,thenyoudon'twriteavalidationtocheckifitisasalesman.Allthismaysoundsilly,butinarealapplication,thissaveslotsoflinesofcodeandmakesyouprogrambetter.Anothergreatbenefitofstrongtypingisthatyouhavebetterdocumentation,asyourcodebecomesyourdocumentation,andit'swaymoreclearwhatyoucanorcan'tdo.

Page 39: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReferentialtransparencyReferentialtransparencyisaconceptwhichworksclosewithpurefunctionsandimmutabilitysinceyourprogramhasfewerassignmentstatements,andoftenwhenyouhaveit,youtendtoneverchangethatvalue.Thisisgreatbecauseyoueliminatesideeffectswiththistechnique.Duringprogramexecution,anyvariablecanbereplacedsincetherearenosideeffects,andtheprogrambecomesreferentiallytransparent.Scalalanguagemakesthisconceptveryclearthemomentyoudeclareavariable.

Page 40: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

InstallingJava8andScala2.11ScalarequiresJVMtowork,soweneedgettheJDK8beforeinstallingScala.GototheOraclewebsite,anddownloadandinstallJDK8fromhttp://www.oracle.com/technetwork/pt/java/javase/downloads/index.html.

Onceyou'vedownloadedJava,weneedtoaddJavatothePATHvariable;otherwise,youcanusetheterminal.Wedothisasfollows:

$cd~/

$wget--no-cookies--no-check-certificate--header"Cookie:

gpw_e24=http%3A%2F%2Fwww.oracle.com%2F;oraclelicense=accept-securebackup-

cookie""

http://download.oracle.com/otn-pub/java/jdk/8u77-b03/jdk-8u77-linux-

i586.tar.gz"

$tar-xzvf$jdk-8u77-linux-x64.tar.gz

$rm-fjdk-8u77-linux-x64.tar.gz

ThenextstepistocreatetheenvironmentvariablecalledJAVA_HOME,andtoputtheJava8binariesinthePATHvariable.InLinux,weneedtoeditthe~/.bashrcfile,andexportthevariablesweneed,likeinthefollowing:

exportJAVA_HOME=~/jdk1.8.0_77

exportPATH=$PATH:$JAVA_HOME/bin

Savethefile,andthenonthesameterminalweneedtosourcethefilevia$source~/.bashrc

NowwecantestourJava8installation.Justtypein$java-version.Youshouldseesomethinglikethefollowing:

$java-version

javaversion"1.8.0_77"

Java(TM)SERuntimeEnvironment(build1.8.0_77-b03)

JavaHotSpot(TM)ServerVM(build25.77-b03,mixedmode)

Let'sgetstarted.WewillbeusingthelatestScalaversion2.11.8.However,thecodeinsidethisbookshouldworkwithanyScala2.11.xversion.Firstofall,let'sdownloadScalafromhttp://www.scala-lang.org/.

ScalaworksonWindows,Mac,andLinux.Forthisbook,IwillshowhowtouseScalaonUbuntuLinux(Debian-based).Openyourbrowserandgotohttp://www.scala-lang.org/download/.

Downloadscala2.11.8:itwillbeaTGZfile.Extractitandaddittoyourpath;otherwise,youcanusetheterminal.Dothisasfollows:

$cd~/

$wgethttp://downloads.lightbend.com/scala/2.11.8/scala-2.11.8.tgz

Page 41: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

$tar-xzvfscala-2.11.8.tgz

$rm-rfscala-2.11.8.tgz

ThenextstepistocreatetheenvironmentvariablecalledSCALA_HOME,andtoputtheScalabinariesinthePATHvariable.InLinux,weneedtoeditthe~/.bashrcfileandexportthevariablesweneed,likeinthefollowing:

exportSCALA_HOME=~/scala-2.11.8/

exportPATH=$PATH:$SCALA_HOME/bin

Savethefile,andthen,onthesameterminal,weneedtosourcethefilevia$source~/.bashrc.

NowwecantestourScalainstallation.Justtypein$scala-version.Youshouldseesomethinglikethefollowing:

$scala-version

Scalacoderunnerversion2.11.8--Copyright2002-2016,LAMP/EPFL

YouhavesuccessfullyinstalledJava8andScala2.11.NowwearereadytostartlearningtheFPprinciplesinScala.Forthis,wewillbeusingtheScalaREPLinthebeginning.ScalaREPLisbundledwiththedefaultScalainstallation,andyoujustneedtotype$scalainyourterminalasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)ServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>

ScalaREPL

Congratulations!YouhaveinstalledJava8andScala2.11successfully.

Page 42: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReadEvalPrintandLoop-REPLReadEvalPrintandLoop(REPL)isalsoknowasalanguageshell.Manyotherlanguageshaveshells,likeLisp,Python,andRubyforinstance.TheREPLisasimpleenvironmenttoexperimentthelanguagein.It'spossibletowriteverycomplexprogramsusingREPL,butthisisnottheREPLgoal.UsingREPLdoesnotinvalidatetheusageofanIDElikeEclipseorIntelliJIDEA.REPLisidealfortestingsimplecommandsandprogramswithouthavingtospendmuchtimeconfiguringprojectslikeyoudowithanIDE.TheScalaREPLallowsyoutocreateavariable,functions,classes,andcomplexfunctionsaswell.Thereisahistoryofeverycommandyouperform;thereissomelevelofautocompletetoo.AsaREPLuser,youcanprintvariablevaluesandcallfunctions.

Page 43: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaHelloWorldusingtheREPLLet'sgetstarted.Goahead,openyourterminal,andtype$scalainordertoopentheScalaREPL.OncetheREPLisopen,youcanjusttype"HelloWorld".Bydoingthis,youperformtwooperations:evalandprint.TheScalaREPLwillcreateavariablecalledres0,andstoreyourStringthere.Thenitwillprintthecontentoftheres0variable.

Page 44: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaREPLHelloWorldprogramWewillseehowtocreateHelloWorldprograminScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>"HelloWorld"

res0:String=HelloWorld

scala>

Scalaisahybridlanguage,whichmeansitisobject-orientedandfunctionalaswell.YoucancreateclassesandobjectsinScala.NextwewillcreateacompleteHelloWorldapplicationusingclasses.

Page 45: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalaobject-orientedHelloWorldprogramWewillseehowtocreateobject-orientedHelloWorldprograminScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>objectHelloWorld{

|defmain(args:Array[String])=println("HelloWorld")

|}

definedobjectHelloWorld

scala>HelloWorld.main(null)

HelloWorld

scala>

Thefirstthingyouneedtorealizeisthatweusethewordobjectinsteadofclass.TheScalalanguagehasdifferentconstructscomparedtoJava.ObjectisasingletoninScala.It'sthesameascodingthesingletonpatterninJava.

NextweseetheworddefthatisusedinScalatocreatefunctions.Intheprecedingprogram,wecreatethemainfunctionsimilartothewaywedoitinJava,andwecallthebuilt-infunctionprintlninordertoprinttheStringHelloWorld.ScalaimportssomeJavaobjectsandpackagesbydefault.CodinginScaladoesnotrequireyoutotype,forinstance,System.out.println("HelloWorld"),butyoucanifyouwant.Let'stakealookatitinthefollowingcode:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>System.out.println("HelloWorld")

HelloWorld

scala>

Wecanandwewilldobetter.Scalahassomeabstractionsforaconsoleapplication,sowecanwritethiscodewithalessernumberoflinesofcode.Toaccomplishthisgoal,weneedtoextendtheScalaclassApp.WhenweextendfromApp,weperforminheritanceandwedon'tneedtodefinethemainfunction.Wecanjustputallthecodeinthebodyoftheclass,whichisveryconvenientandmakesthecodecleanandsimpletoread.

Page 46: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaHelloWorldAppintheScalaREPLWewillseehowtocreateScalaHelloWorldAppinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>objectHelloWorldextendsApp{

|println("HelloWorld")

|}

definedobjectHelloWorld

scala>HelloWorld

objectHelloWorld

scala>HelloWorld.main(null)

HelloWorld

scala>

AftercodingtheHelloWorldobjectintheScalaREPLwecanasktheREPLwhatHelloWorldis,andasyoumightrealize,theREPLwillanswerthatHelloWorldisanobject.ThisisaveryconvenientScalawaytocodeconsoleapplications,becausewecanhaveaHelloWorldapplicationwithjustthreelinesofcode.Sadly,tohavethesameprograminJava,itrequiredwaymorecode.Javaisagreatlanguageforperformance,butitisaverboselanguagecomparedwithScala,forinstance.

Page 47: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JavaHelloWorldapplicationWewillseehowtocreateJavaHelloWorldapplicationasfollows:

packagescalabook.javacode.chap1;

publicclassHelloWorld{

publicstaticvoidmain(Stringargs[]){

System.out.println("HellowWorld");

}

}

TheJavaapprequiredsixlinesofcode,whileinScala,wewereabletodothesamewith50%lesscode(threelinesofcode).Thisisaverysimpleapplication.Whenwearecodingcomplexapplications,thisdifferencegetsbigger,asaScalaapplicationendsupwithwaylesscodethanJava.

Remember,weuseanobjectinScalainordertohaveaSingleton(DesignPatternthatmakessureyouhavejustoneinstanceofaclass),andifwewantthesameinJava,thecodewouldbesomethinglikethefollowing:

packagescalabook.javacode.chap1;

publicclassHelloWorldSingleton{

privateHelloWorldSingleton(){}

privatestaticclassSingletonHelper{

privatestaticfinalHelloWorldSingletonINSTANCE=

newHelloWorldSingleton();

}

publicstaticHelloWorldSingletongetInstance(){

returnSingletonHelper.INSTANCE;

}

publicvoidsayHello(){

System.out.println("HelloWorld");

}

publicstaticvoidmain(String[]args){

getInstance().sayHello();

}

}

It'snotjustaboutthesizeofthecode,butalsoaboutconsistencyandthelanguageprovidingmoreabstractionsforyou.Ifyouwritelesscode,youwillhavefewerbugsinyoursoftwareattheendoftheday.

Page 48: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalalanguage-thebasicsScalaisastaticallytypedlanguagewithaveryexpressivetypesystemwhichenforcesabstractionsinasafeyetcoherentmanner.AllvaluesinScalaareJavaobjects(primitiveswhichareunboxedatruntime),becauseattheendoftheday,ScalarunsontheJavaJVM.ScalaenforcesimmutabilityasacoreFPprinciple.ThisenforcementhappensinmultipleaspectsoftheScalalanguage,forinstance,whenyoucreateavariable,youdoitinanimmutableway,whenyouuseancollection,youwilluseaimmutablecollection.Scalaalsoletsyouusemutablevariablesandmutablestructures,butbydesign,itfavorsimmutableones.

Page 49: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalavariables-varandvalWhenyouarecodinginScala,youcreatevariablesusingtheoperatorvar,oryoucanusetheoperatorval.Theoperatorvarallowsyoutocreateamutablestate,whichisfineaslongasyoumakeitlocal,followtheCORE-FPprinciplesandavoidamutablesharedstate.

Page 50: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaREPLvarusageWewillseehowtousevarinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>varx=10

x:Int=10

scala>x

res0:Int=10

scala>x=11

x:Int=11

scala>x

res1:Int=11

scala>

However,Scalahasamoreinterestingconstructcalledval.Usingthevaloperatormakesyourvariablesimmutable,andthismeansyoucan'tchangethevalueonceyou'vesetit.IfyoutrytochangethevalueofthevalvariableinScala,thecompilerwillgiveyouanerror.AsaScaladeveloper,youshouldusethevariablevalasmuchaspossible,becausethat'sagoodFPmindset,anditwillmakeyourprogramsbetter.InScala,everythingisanobject;therearenoprimitives--thevarandvalrulesapplyforeverythingitcouldbutanIntorStringorevenaclass.

Page 51: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalavalusageattheScalaREPLWewillseehowtousevalinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx=10

x:Int=10

scala>x

res0:Int=10

scala>x=11

<console>:12:error:reassignmenttoval

x=11

^

scala>x

res1:Int=10

scala>

Page 52: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingimmutablevariablesRightnow,let'sseehowwedefinethemostcommontypesinScalasuchasInt,Double,Boolean,andString.Remember,youcancreatethesevariablesusingvalorvardependingonyourneeds.

Page 53: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalavariabletypeintheScalaREPLWewillseeScalavariabletypeinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx=10

x:Int=10

scala>valy=11.1

y:Double=11.1

scala>valb=true

b:Boolean=true

scala>valf=false

f:Boolean=false

scala>vals="ASimpleString"

s:String=ASimpleString

scala>

Forthevariablesintheprecedingcode,wedidnotdefinethetype.Scalalanguagefiguresitoutforus.However,itispossibletospecifythetypeifyouwant.InScala,thetypecomesafterthenameofthevariable.

Page 54: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalavariableswithexplicittypingattheScalaREPLWewillseeScalavariableswithexplicittypingatScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx:Int=10

x:Int=10

scala>valy:Double=11.1

y:Double=11.1

scala>vals:String="MyString"

s:String="MyString"

scala>valb:Boolean=true

b:Boolean=true

scala>

Page 55: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaconditionalandloopsstatementsLikeanyotherlanguage,Scalahassupportforconditionalstatementslikeifandelse.WhileJavahasaswitchstatement,ScalahasamorepowerfulandfunctionalstructurecalledPatternMatcher,whichwewillcoverlaterinthischapter.Scalaallowsyoutouseifstatementsduringvariableassignments,whichisverypracticalaswellasuseful.

Page 56: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IfstatementsinScalaREPLWewillseehowtouseifstatementsinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx=10

x:Int=10

scala>if(x==10)

|println("Xis10")

Xis10

scala>valy=if(x==10)11

y:AnyVal=11

scala>y

res1:AnyVal=11

scala>

Intheprecedingcode,youcanseethatwesetthevariableybasedonanifcondition.Scalaifconditionsareverypowerful,andtheyalsocanbeusedinreturnstatements.

Page 57: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IfstatementsinreturnstatementsinScalaREPLWewillseehowtouseifstatementsinreturnstatementsinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx=10

x:Int=10

scala>defsomeFunction=if(x==10)"Xis10"

someFunction:Any

scala>someFunction

res0:Any=Xis10

scala>

Scalasupportselsestatementstoo,andyoualsocanusetheminvariablesandreturnstatementsaswellasfollows:

~$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valx=10

x:Int=10

scala>if(x==10){

|println("Xis10")

|}else{

|println("Xissomethingelse")

|}

Xis10

scala>

NowyouwilllearnhowtouseforloopsinScala.ForloopsareverypowerfulinScala.Wewillstartwiththebasicsandlaterwewillmoveontofunctionalloopsusedforcomprehensions,alsoknowasListcomprehensions.

InScala,forloopsworkwithranges,whichisanotherScaladatastructurethatrepresentsnumbersfromastartingpointtoanendpoint.Therangeiscreatedusingtheleftarrowoperator(<-).Scalaallowsyoutohavemultiplerangesinthesameforloopaslongasyouusethesemicolon(;).

Youalsocanuseifstatementsinordertofilterdatainsideforloops,andworksmoothlywithListstructures.Scalaallowsyoutocreatevariablesinsideaforloopaswell.Rightnow,let'sseesomecodewhichillustratesthevariousforloopusagesinScalalanguage.

Page 58: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BasicforloopinScalaREPLWewillseehowtousebasicforloopinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>for(i<-1to10)

|println("i*"+i+"="+i*10)

i*1=10

i*2=20

i*3=30

i*4=40

i*5=50

i*6=60

i*7=70

i*8=80

i*9=90

i*10=100

scala>

Rightnow,wewillcreateaforloopusingaScaladatastructurecalledList.Thisisveryuseful,becauseinthefirstlineofcode,youcandefineaListaswellassetitsvaluesinthesameline.SinceweareusingtheListstructure,youdon'tneedtopassanyotherargumentbesidestheListitself.

Page 59: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ForwithListinScalaREPLWewillseehowtouseforwithListinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>vallistOfValues=List(1,2,3,4,5,6,7,8,9,10)

listOfValues:List[Int]=List(1,2,3,4,5,6,7,8,9,10)

scala>for(i<-listOfValues)println(i)

1

2

3

4

5

6

7

8

9

10

scala>

Next,wecanuseforloopswithifstatementsinordertoapplysomefiltering.Laterinthisbook,wewillapproachamorefunctionalwaytoapproachfilteringusingfunctions.Forthiscode,let'ssaywewanttogetjusttheevennumbersonthelistandprintthem.

Page 60: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Forwithifstatementsforfiltering-ScalaREPLWewillseehowtouseforwithifstatementsinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>vallistOfValues=List(1,2,3,4,5,6,7,8,9,10)

listOfValues:List[Int]=List(1,2,3,4,5,6,7,8,9,10)

scala>for(i<-listOfValues)if(i%2==0)println(i)

2

4

6

8

10

scala>

Page 61: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JavacodeforfilteringevennumbersInScalalanguage,wejustneedtwolinesofcodetoperformthisfiltering,whereasinJavaitwouldhaverequiredatleastelevenlinesofcodeasyouseeinthefollowingcode:

packagescalabook.javacode.chap1;

importjava.util.Arrays;

importjava.util.List;

publicclassForLoopsEvenNumberFiltering{

publicstaticvoidmain(String[]args){

List<Integer>listOfValues=Arrays.asList(

newInteger[]{1,2,3,4,5,6,7,8,9,10});

for(Integeri:listOfValues){

if(i%2==0)System.out.println(i);

}

}

}

Page 62: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ForcomprehensionsAlsoknownaslistorsequencecomprehensions,forcomprehensionsareoneoftheFPwaystoperformloops.ThisisalanguagesupporttocreateListstructureorcollectionsbasedonothercollections.ThistaskisperformedinaSetBuildernotation.AnotherwaytoaccomplishthesamegoalwouldbebyusingtheMapandfilterfunctions,whichwewillcoverlaterinthischapter.Forcomprehensionscanbeusedinageneratorform,whichwouldintroducenewvariablesandvalues,orinareductionistway,whichwouldfiltervaluesresultingintoanewcollectionorsequence.Thesyntaxis:for(expt)yielde,wheretheyieldoperatorwilladdnewvaluestoanewcollection/sequencethatwillbecreatedfromtheoriginalsequence.

Page 63: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ForcomprehensioninScalaREPLWewillseehowtouseforcomprehensioninScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valnames=Set("Diego","James","John","Sam","Christophe")

names:scala.collection.immutable.Set[String]=Set(John,Sam,Diego,James,

Christophe)

scala>

scala>valbrazilians=for{

|name<-names

|initial<-name.substring(0,1)

|}yieldif(name.contains("Die"))name

brazillians:scala.collection.immutable.Set[Any]=Set((),Diego)

scala>

Intheprecedingcode,wecreateasetofnames.Asyoucansee,Scala,bydefault,prefersimmutabledatastructuresandusesimmutable.Set.Whenweapplytheforloop,wearesimplyfilteringonlythenameswhichcontainaspecificsubstring,andthen,usingtheyieldoperator,wearecreatinganewSetstructure.Theyieldoperatorwillkeepthestructureyouareusing.Forinstance,ifweuseListstructure,itwouldcreateaListinsteadofaSetstructure,theyieldoperatorwillalwayskeepthesamedatacollectionyouhaveonthevariable.AnotherinterestingaspectoftheprecedingcodeisthefactthatweareholdingtheresultoftheforcomprehensioninavariablecalledBrazilians.Javadoesnothaveforcomprehensions,butwecouldusesimilarcodealthoughitwouldrequirewaymorelinesofcode.

Page 64: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JavacodeforperformingfilteringwithcollectionsWewillseehowtouseJavacodeforperformingfilteringwithcollectionsasfollows:

packagescalabook.javacode.chap1;

importjava.util.LinkedHashSet;

importjava.util.Set;

publicclassJavaNoForComprehension{

publicstaticvoidmain(String[]args){

Set<String>names=newLinkedHashSet<>();

Set<String>brazillians=newLinkedHashSet<>();

names.add("Diego");

names.add("James");

names.add("John");

names.add("Sam");

names.add("Christophe");

for(Stringname:names){

if(name.contains("Die"))brazillians.add(name);

}

System.out.println(brazillians);

}

}

Page 65: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalacollectionsIntheprevioussection,wesawhowtocreatetheListandSetstructuresinScalainanimmutableway.NowwewilllearntoworkwiththeListandSetstructuresinamutableway,andalsowithothercollectionssuchassequences,tuples,andMaps.Let'stakealookattheScalacollectionshierarchytree,asshowninthefollowingdiagram:

Nowlet'stakealookattheScalaSeqclasshierarchy.Asyoucansee,Seqistraversableaswell.

Page 66: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalacollectionsextendfromtraversable,whichisthemaintraitofallcollection'sdescends.Liststructures,forinstance,extendfromSeqclasshierarchy,whichmeanssequence-Listisakindofsequence.AllthesetreesareimmutableormutabledependingontheScalapackageyouendupusing.

Let'sseehowtoperformbasicmutableoperationswithListstructuresinScala.Inordertohavefilterandremovaloperations,weneeduseaBuffersequenceasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>varms=scala.collection.mutable.ListBuffer(1,2,3)

ms:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3)

scala>ms+=4

res0:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3,4)

scala>ms+=5

res1:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3,4,5)

scala>ms+=6

res2:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3,4,5,6)

scala>ms(1)

res3:Int=2

scala>ms(5)

res4:Int=6

scala>ms-=5

res5:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3,4,6)

scala>ms-=6

Page 67: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

res6:scala.collection.mutable.ListBuffer[Int]=ListBuffer(1,2,3,4)

scala>

Let'sseethenextsetofcode.

Page 68: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Creating,removing,andgettinganitemfromamutablelistinScalaREPLWewillseehowtocreate,remove,andgetanitemfromamutablelistinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>varnames=scala.collection.mutable.SortedSet[String]("Diego",

"Poletto","Jackson")

names:scala.collection.mutable.SortedSet[String]=TreeSet(Diego,Jackson,

Poletto)

scala>names+="Sam"

res2:scala.collection.mutable.SortedSet[String]=TreeSet(Diego,Jackson,

Poletto,Sam)

scala>names("Diego")

res4:Boolean=true

scala>names-="Jackson"

res5:scala.collection.mutable.SortedSet[String]=TreeSet(Diego,Poletto,

Sam)

scala>

Haveyoueverwantedtoreturnmultiplevaluesinamethod?Well,inJavayouhavetocreateaclass,butinScala,thereisamoreconvenientwaytoperformthistask,andyouwon'tneedtocreatenewclasseseachtime.Tuplesallowyoutoreturnorsimplyholdmultiplevaluesinmethodswithouthavingtocreateaspecifictype.

Page 69: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalatuplesWewillseeScalatuplesasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valconfig=("localhost",8080)

config:(String,Int)=(localhost,8080)

scala>config._1

res0:String=localhost

scala>config._2

res1:Int=8080

scala>

Scalahasspecialmethodscalled_1and_2whichyoucanusetoretrieveatuple'svalues.Theonlythingyouhavetokeepinmindisthefactthatvaluesarekeptintheorderofinsertioninthetuple.

Scalahasaverypracticalandusefulcollectionlibrary.AMap,forinstance,isakey/valuepairthatcanberetrievedbasedonthekey,whichisunique.However,Mapvaluesdonotneedtobeunique.LikeotherScalacollections,youhavemutableandimmutableMapcollections.KeepinmindthatScalafavorsimmutablecollectionsovermutableones.

Page 70: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaimmutableMapinScalaREPLWewillseeScalaimmutableMapinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valnumbers=Map("one"->1,

|"two"->2,

|"three"->3,

|"four"->4,

|"five"->5,

|"six"->6,

|"seven"->7,

|"eight"->8,

|"nine"->9,

|"ten"->10)

numbers:scala.collection.immutable.Map[String,Int]=Map(four->4,three->

3,two->2,six->6,seven->7,ten->10,five->5,nine->9,one->1,

eight->8)

scala>

scala>numbers.keys

res0:Iterable[String]=Set(four,three,two,six,seven,ten,five,nine,

one,eight)

scala>

scala>numbers.values

res1:Iterable[Int]=MapLike(4,3,2,6,7,10,5,9,1,8)

scala>

scala>numbers("one")

res2:Int=1

scala>

Asyoucansee,Scalausesscala.collection.immutable.MapwhenyoucreateaMapusingMap().Bothkeysandvaluesareiterable,andyoucanhaveaccesstoallthekeyswiththekeysmethodortoallthevaluesusingthevaluesmethod.

Page 71: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalamutableMapsatScalaREPLWewillseeScalamutableMapinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valmap=scala.collection.mutable.HashMap.empty[Int,String]

map:scala.collection.mutable.HashMap[Int,String]=Map()

scala>map+=(1->"one")

res0:map.type=Map(1->one)

scala>map+=(2->"two")

res1:map.type=Map(2->two,1->one)

scala>map+=(3->"three")

res2:map.type=Map(2->two,1->one,3->three)

scala>map+=(4->"mutable")

res3:map.type=Map(2->two,4->mutable,1->one,3->three)

scala>

Ifyouaredealingwithmutablestate,youhavetobeexplicitandthisisgreatinScala,becauseitincreasesdevelopers'awarenessandavoidsmutablesharedstatebydefault.So,inordertohaveamutableMap,weneedtoexplicitlycreatetheMapwithscala.collection.mutable.HashMap.

Page 72: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MonadsMonadsarecombinableparametrizedcontainertypeswhichhavesupportforhigher-orderfunctions.Rememberhigher-orderfunctionsarefunctionswhichreceivefunctionsasparametersandreturnfunctionsasresults.OneofthemostusedfunctionsinFPisMap.Maptakesafunction,appliesittoeachelementinthecontainer,andreturnsanewcontainer.

Page 73: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaMapfunctioninScalaREPLWewillseeMapfunctioninScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>

scala>valnumbers=List(1,2,3,4,5,6)

numbers:List[Int]=List(1,2,3,4,5,6)

scala>defdoubleIt(i:Int):Double=i*2

doubleIt:(i:Int)Double

scala>valdoubled=numbers.map(doubleIt_)

doubled:List[Double]=List(2.0,4.0,6.0,8.0,10.0,12.0)

scala>valdoubled=numbers.map(2.0*_)

doubled:List[Int]=List(2.0,4.0,6.0,8.0,10.0,12.0)

scala>

Intheprecedingcode,wecreatedalistofnumberscontaining1,2,3,4,5,and6.WealsodefinedaScalafunctioncalleddoubleIt,whichreceivesanintegerandmultipliesitby2.0resultinginadoublenumber.ThemapfunctioncallsdoubleItforeachelementintheList(1,2,3,4,5,6),andtheresultisanewcontainer,abrandnewListinstancecontainingthenewvalues.

Scalahassomesyntacticalsugarwhichhelpsustobemoreproductive.Forinstance,youmayrealizethatinthepreviouscode,wealsodid-2.0*_.Theunderscoreisaspecialoperatorforthisspecificcase--itmeansthecurrentvalueisbeingiteratedintothecollection.Scalawillcreateafunctionfromthisexpressionforus.

Asyoumighthaverealized,mapfunctionsareprettyusefulforlotsofreasons:onereasonisthatyoucandocomplexcomputationswithoutusingtheforloopexplicitly,andthismakesyourcodefunctional.Secondly,wecanuseamapfunctiontoconvertelementtypesfromonetypetoanother.That'swhatwedidinthepreviouscode:wetransformedalistofintegersintoalistofdoubles.Lookatthefollowing:

scala>valone=Some(1)

one:Some[Int]=Some(1)

scala>valoneString=one.map(_.toString)

oneString:Option[String]=Some(1)

Themapfunctionoperatesoverseveraldatastructuresandnotonlycollections,asyoucanseeinthepreviouscode.YoucanusethemapfunctiononprettymucheverythinginScalalanguage.

Themapfunctionisgreat,butyoucanendupwithnestedstructures.That'swhy,whenweareworkingwithMonads,weuseaslightlydifferentversionofthemapfunctioncalledflatMap,whichworksinaverysimilarwaytothemapfunction,butreturnsthevaluesinaflatforminsteadofnestedvalues.

Page 74: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Inordertohaveamonad,youneedtohaveamethodcalledflatMap.OtherfunctionlanguagessuchasHaskellcallflatMapasbind,andusetheoperator>>=.Thesyntaxchangeswiththelanguage,buttheconceptisthesame.

Monadscanbebuiltindifferentways.InScala,weneedasingleargumentconstructorwhichwillworkasamonadfactory.Basically,theconstructorreceivesonetype,A,andreturnsMonad[A]orjustM[A].Forinstance,unit(A)foraListwillbe==List[A]andunit(A),whereaisanOption==Option[A].InScala,youdon'tneedtohaveunit;thisisoptional.TohaveamonadinScala,youneedtohavemapandflatMapimplemented.

WorkingwithMonadswillmakeyouwritealittlebitmorecodethanbefore.However,youwillgetawaybetterAPI,whichwillbeeasiertoreuseandyourpotentialcomplexitywillbemanaged,becauseyouwon'tneedtowriteacomplexcodefullofifandforloops.Thepossibilitiesareexpressedthroughthetypes,andthecompilercancheckitforyou.LetusseeasimplemonadexampleinScalalanguage:

Page 75: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

OptionMonadinScalaWewillseeoptionMonadinScalaasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>vala:Option[Int]=Some(1)

a:Option[Int]=Some(1)

scala>a.get

res0:Int=1

scala>valb:Option[Int]=None

b:Option[Int]=None

scala>b.get

java.util.NoSuchElementException:None.get

atscala.None$.get(Option.scala:347)

atscala.None$.get(Option.scala:345)

...32elided

scala>b.getOrElse(0)

res2:Int=0

scala>a==b

res3:Boolean=false

scala>

InHaskell,thisalsoknownastheMaybemonad.Optionmeansoptionalvalue,becausewearenot100%sureifthevaluewillbepresent.Inordertoexpressavalue,weusetheSometype,andinordertoexpressthelackofvalue,weusenone.OptionMonadsaregreat,theymakeyourcodemoreexplicit,becauseamethodmightreceiveorreturnanoption,whichmeansyouareexplicitlysayingthiscouldbenull.However,thistechniqueisnotonlymoreexpressivebutalsosafer,sinceyouwon'tgetanullpointer,becauseyouhaveacontaineraroundthevalue.Although,ifyoucallthemethodgetinOptionanditisnone,youwillgetaNoSuchelementException.Inordertofixthis,youcanusethemethodgetOrElse,andyoucansupplyafallbackvaluewhichwillbeusedinthecaseofnone.Alright,butyoumightbewonderingwheretheflatMapmethodis.Don'tworry,ScalaimplementsthismethodforusintotheOptionabstraction,soyoucanuseitwithnoissues.

scala>valc=Some("one")

c:Some[String]=Some(one)

scala>c.flatMap(s=>Some(s.toUpperCase))

res6:Option[String]=Some(ONE)

TheScalaREPLcanperformautocompleteforyou.IfyoutypeC+Tab,youwillseealltheavailablemethodsfortheSomeclass.Themapfunctionisavailableforyoutouse,andasIsaidbefore,thereisnounitfunctioninScalawhatsoever.However,itisnotwrongifyouaddinyourAPIs.

Page 76: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AlistofallmethodsusingtheScalaREPLFollowingarethelistofallmethodsusingtheScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valc=Some("one")

c:Some[String]=Some(one)

scala>c.

++countforeachiteratorproductArity

seqtoBufferunzip

++:dropgenericBuilderlastproductElement

sizetoIndexedSequnzip3

/:dropRightgetlastOptionproductIterator

slicetoIterableview

:\dropWhilegetOrElsemapproductPrefix

slidingtoIteratorwithFilter

WithFilterequalsgroupBymax

reducespantoLeftx

addStringexistsgroupedmaxBy

reduceLeftsplitAttoListzip

aggregatefilterhasDefiniteSizeminreduceLeftOption

stringPrefixtoMapzipAll

canEqualfilterNothashCodeminByreduceOption

sumtoRightzipWithIndex

collectfindheadmkStringreduceRight

tailtoSeq

collectFirst

flatMapheadOptionnonEmptyreduceRightOptiontails

toSet

companionflatten

initorElsereprtake

toStream

containsfoldinitsorNull

sameElementstakeRighttoString

copyfoldLeftisDefinedpar

scantakeWhiletoTraversable

copyToArrayfoldRightisEmptypartitionscanLeft

totoVector

copyToBufferforallisTraversableAgainproductscanRight

toArray

transpose

scala>c

Page 77: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalaclass,traits,andOOprogrammingAsahybridpost-functionallanguage,ScalaallowsyoutowriteOOcodeandcreateclassesaswell.Rightnowwewilllearnhowtocreateclassesandfunctionsinsideclasses,andalsohowtoworkwithtraits,whicharesimilartoJavainterfacesinconceptbutwaymorepowerfulinpractice.

Page 78: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AsimpleScalaclassinScalaREPLWewillseeasimpleScalaclassinScalaREPLasfollows:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>classCalculator{

|defadd(a:Int,b:Int):Int=a+b

|defmultiply(n:Int,f:Int):Int=n*f

|}

definedclassCalculator

scala>

scala>valc=newCalculator

c:Calculator=Calculator@380fb434

scala>c.add(1,2)

res0:Int=3

scala>c.multiply(3,2)

res1:Int=6

scala>

Atfirstglance,theprecedingcodelookslikeJava.Butlet'saddconstructors,getters,andsetters,andthenyoucanseehowmuchwecanaccomplishwithjustafewlinesofcode.

Page 79: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaplainoldJavaobjectinScalaREPLFollowingisaScalaplainoldJavaobjectinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>classPerson(

|@scala.beans.BeanPropertyvarname:String="",

|@scala.beans.BeanPropertyvarage:Int=0

|){

|name=name.toUpperCase

|overridedeftoString="name:"+name+"age:"+age

|}

definedclassPerson

scala>

scala>valp=newPerson("Diego",31)

p:Person=name:DIEGOage:31

scala>valp1=newPerson(age=31,name="Diego")

p1:Person=name:DIEGOage:31

scala>p.getAge

res0:Int=31

scala>p1.getName

res1:String=DIEGO

scala>

ConstructorsinScalaarejustlinesofcode.Youmightrealizethatwegetthenamevariable,andapplyafunctiontochangethegivennametouppercaseintheprecedingexample.Ifyouwant,youcanputasmanylinesasyouwant,andyoucanperformasmanycomputationsasyouwish.

Onthissamecode,weperformmethodoverridingaswell,becauseweoverridethetoStringmethod.InScala,inordertodoanoverride,youneedtousetheoverrideoperatorinfrontofthefunctiondefinition.

WejustwroteaPlainOldJavaObject(POJO)withveryfewlinesofcodeinScala.Scalahasaspecialannotationcalled@scala.beans.BeanProperty,whichgeneratesthegetterandsettermethodforyou.Thisisveryuseful,andsaveslotsoflinesofcode.However,thetargetneedstobepublic;youcan'taapplyBeanPropertyannotationontopofaprivatevarorvalobject.

Page 80: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PersonclassinJavaFollowingisaPersonclassinJava:

packagescalabook.javacode.chap1;

publicclassJavaPerson{

privateStringname;

privateIntegerage;

publicJavaPerson(){}

publicJavaPerson(Stringname,Integerage){

super();

this.name=name;

this.age=age;

}

publicJavaPerson(Stringname){

super();

this.name=name;

}

publicJavaPerson(Integerage){

super();

this.age=age;

}

publicIntegergetAge(){

returnage;

}

publicvoidsetAge(Integerage){

this.age=age;

}

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

this.name=name;

}

}

Page 81: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TraitsandinheritanceIt'spossibletodoinheritanceinScalaaswell.Forsuchatask,youusetheoperatorextendaftertheclassdefinition.Scalajustallowsyoutoextendoneclass,justlikeJava.JavadoesnotallowmultipleinheritancelikeC++.However,ScalaallowsitbyusingtheMixingtechniquewithtraits.ScalatraitsarelikeJavainterface,butyoucanalsoaddconcretecode,andyouareallowedtohaveasmanytraitsasyouwantinyourcode.

Page 82: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalainheritancecodeinScalaREPLFollowingisaScalainheritancecodeinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>classPerson(

|@scala.beans.BeanPropertyvarname:String="",

|@scala.beans.BeanPropertyvarage:Int=0

|){

|name=name.toUpperCase

|overridedeftoString="name:"+name+"age:"+age

|}

definedclassPerson

scala>

scala>classLowerCasePerson(name:String,age:Int)extendsPerson(name,age){

|setName(name.toLowerCase)

|}

definedclassLowerCasePerson

scala>

scala>valp=newLowerCasePerson("DIEGOPACHECO",31)

p:LowerCasePerson=name:diegopachecoage:31

scala>p.getName

res0:String=diegopacheco

scala>

ScaladoesnotmakeconstructorsinheritancelikeJava.Soyouneedtorewritetheconstructorsandpassthevaluesthroughasuperclass.Allcodeinsidetheclasswillbethesecondaryconstructor.Allcodeinsideparentheses()intheclassdefinitionwillbetheprimaryconstructor.It'spossibletohavemultipleconstructorsusingthethisoperator.Forthisparticularimplementation,wechangedthedefaultbehaviorandaddednewconstructorcodeinordertomakethegivennamelowercase,insteadofthedefaultuppercasedefinedbythePersonsuperclass.

Page 83: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalatraitssamplecodeinScalaREPLFollowingisaScalatraitssamplecodeinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>traitCar

definedtraitCar

scala>

scala>traitSportCar{

|valbrand:String

|defrun():String="RghhhhhRghhhhhRghhhhh...."

|}

definedtraitSportCar

scala>

scala>traitPrintable{

|defprintIt:Unit

|}

definedtraitPrintable

scala>

scala>classBMWextendsCarwithSportCarwithPrintable{

|overridevalbrand="BMW"

|overridedefprintIt:Unit=println(brand+"does"+run())

|}

definedclassBMW

scala>

scala>valx1=newBMW

x1:BMW=BMW@22a71081

scala>x1.printIt

BMWdoesRghhhhhRghhhhhRghhhhh....

scala>

Intheprecedingcode,wecreatedmultipletraits.OneiscalledCar,whichisthemothertrait.Traitssupportinheritanceaswell,andwehaveitwiththeSportCartraitwhichextendsfromtheCartrait.TheSportCartraitdemandsavariablecalledbrand,anddefinesaconcreteimplementationofthefunctionrun.Finally,wehaveaclasscalledBMWwhichextendsfrommultipletraits--thistechniqueiscalledmixing.

Page 84: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalatraitsusingvariablemixingtechniqueatScalaREPLFollowingisaScalatraitsusingvariablemixingtechniqueatScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>traitSportCar{

|defrun():String="RghhhhhRghhhhhRghhhhh...."

|}

definedtraitSportCar

scala>

scala>valbmw=newObjectwithSportCar

bmw:SportCar=$anon$1@ed17bee

scala>bmw.run

res0:String=RghhhhhRghhhhhRghhhhh....

scala>

Scalaisaverypowerfullanguageindeed.It'spossibletoaddtraitstoavariableatruntime.Whenyoudefineavariable,youcanusethewithoperatoraftertheassignment.Thisisaveryusefulfeature,becauseitmakesiteasiertomakefunctioncomposition.Youcanhavemultiplespecializedtraitsandjustaddtheminyourvariablesasyouneedthem.

Scalaallowsyoutocreatethetypealiasaswell,thisisaverysimpletechniquewhichwillincreasethereadabilityofyourcode.It'sjustasimplealias.

Page 85: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalatypealiassampleinScalaREPLFollowingisaScalatypealiassampleinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>typeEmail=String

definedtypealiasEmail

scala>

scala>vale=newEmail("[email protected]")

e:[email protected]

scala>

WhenyouarecodingwithScala,itishighlyrecommendedthatyouusethetypealiasandtraitsforeverything,becausethatwayyouwillgetmoreadvantageswithyourcompiler,andyouwillavoidwritingunnecessarycodeandunnecessaryunittests.

Page 86: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CaseclassesWearenotdoneyetintermsoftheOOfeaturesinScala;thereisanotherveryinterestingwaytoworkwithclassesinScala:theso-calledcaseclasses.CaseclassesaregreatbecauseyoucanhaveaclasswithwaylessnumberoflinesofcodeandcaseclassescanbepartofaPatternMatcher.

Page 87: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalacaseclassesfeatureinScalaREPLFollowingisaScalacaseclassesfeatureinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>caseclassPerson(name:String,age:Int)

definedclassPerson

scala>valp=Person("Diego",31)

p:Person=Person(Diego,31)

scala>valp2=Person("Diego",32)

p2:Person=Person(Diego,32)

scala>p.name

res0:String=Diego

scala>p.age

res1:Int=31

scala>p==p

res2:Boolean=true

scala>p.toString

res3:String=Person(Diego,31)

scala>p.hashCode

res4:Int=668670772

scala>p.equals(p2)

res5:Boolean=false

scala>p.equals(p)

res6:Boolean=true

scala>

ThisistheScalawaytoworkwithclasses.Becausethisissomucheasierandcompact,youprettymuchcreateaclasswithonelineofcode,andyoucanhavetheequalsandhashcodemethodsforfree.

Page 88: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PatternMatcherWhenyoucodeinJava,youcanuseaSwitchstatement.However,inScala,wehaveamorepowerfulfeaturecalledPatternMatcher,whichisakindofswitchbutonsteroids.

Page 89: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SimplePatternMatcherinScalaFollowingisaSimplePatternMatcherinScala:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>defresolve(choice:Int):String=choicematch{

|case1=>"yes"

|case0=>"no"

|case_=>thrownewIllegalArgumentException("Validargumentsare:

0or1.Yourargis:

"+choice)

|}

resolve:(choice:Int)String

scala>println(resolve(0))

no

scala>println(resolve(1))

yes

scala>try{

|println(resolve(33))

|}catch{

|casee:Exception=>println("SomethingWentWorng.EX:"+e)

|}

SomethingWentWorng.EX:java.lang.IllegalArgumentException:Validarguments

are:0or1.Yourargis:33

scala>

ScalausesPatternMatcherforerrorhandling.JavadoesnothavePatternMatcherlikeScala.It'ssimilartoaswitchstatement;however,PatternMatchercanbeusedinamethodreturnstatementasyoucanseeintheprecedingcode.Scaladeveloperscanspecifyaspecialoperatorcalled_(Underscore),whichallowsyoutospecifyanythinginthePatternMatcherscope.Thisbehaviorissimilartoelseinanifconditional.However,inScala,youcanuse_inseveralplaces,andnotonlyastheotherwiseclause,likeinJavaswitch.

ErrorhandlinginScalaissimilartoerrorhandlinginJava.Weusetry...catchblocks.ThemaindifferenceisthatyouhavetousePatternMatcherinScala,whichisgreatbecauseitaddsmoreflexibilitytoyourcode.PatternMatcherinScalacanoperateagainstmanydatastructureslikecaseclasses,collections,integers,andstrings.

Theprecedingcodeisprettysimpleandstraightforward.NextwewillseeamorecomplexandadvancedcodeusingtheScalaPatternMatcherfeature.

Page 90: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AdvancedpatternmatcherinScalaREPLFollowingisanAdvancedPatternMatcherusingScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>deffactorial(n:Int):Int=nmatch{

|case0=>1

|casen=>n*factorial(n-1)

|}

factorial:(n:Int)Int

scala>

scala>println(factorial(3))

6

scala>println(factorial(6))

720

scala>

PatternMatchercanbeusedinaveryfunctionalway.Forinstance,intheprecedingcode,weusethePatternMatcherforrecursion.Thereisnoneedtocreateavariabletostoretheresult,wecanputthePatternMatcherstraighttothefunctionreturn,whichisveryconvenientandsaveslotsoflinesofcode.

Page 91: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AdvancedcomplexpatternmatcherinScalaREPLFollowingisanAdvancedcomplexPatternMatcherusingScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>traitColor

definedtraitColor

scala>caseclassRed(saturation:Int)extendsColor

definedclassRed

scala>caseclassGreen(saturation:Int)extendsColor

definedclassGreen

scala>caseclassBlue(saturation:Int)extendsColor

definedclassBlue

scala>defmatcher(arg:Any):String=argmatch{

|case"Scala"=>"AAwesomeLanguage"

|casex:Int=>"AnIntwithvalue"+x

|caseRed(100)=>"Redsat100"

|caseRed(_)=>"AnykindofREDsat"

|caseGreen(s)ifs==233=>"Greensat233"

|caseGreen(s)=>"Greensat"+s

|casec:Color=>"SomeColor:"+c

|casew:Any=>"Whatever:"+w

|}

matcher:(arg:Any)String

scala>println(matcher("Scala"))

AAwesomeLanguage

scala>println(matcher(1))

AnIntwithvalue1

scala>println(matcher(Red(100)))

Redsat100

scala>println(matcher(Red(160)))

AnykindofREDsat

scala>println(matcher(Green(160)))

Greensat160

scala>println(matcher(Green(233)))

Greensat233

scala>println(matcher(Blue(111)))

SomeColor:Blue(111)

scala>println(matcher(false))

Whatever:false

scala>println(matcher(newObject))

Whatever:java.lang.Object@b56c222

scala>

TheScalaPatternMatcherisreallyamazing.WejustusedanifstatementinthemiddleofthePatternMatcher,andalso_tospecifyamatchforanykindofredvalue.WealsousedcaseclassesinthemiddleofthePatternMatcherexpressions.

Page 92: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PartialfunctionsPartialfunctionsaregreatforfunctioncomposition.TheycanoperatewithcasestatementsaswejustlearnedfromPatternMatcher.Partialfunctionsaregreatinthesenseoffunctioncomposition.Theyallowustodefineafunctioninsteps.Scalaframeworksandlibrariesusethisfeaturealottocreateabstractionsandcallbackmechanisms.It'salsopossibletocheckifapartialfunctionisbeingsuppliedornot.

Partialfunctionsarepredictable,becausethecallercancheckbeforehandifthevaluewillbeappliedtothepartialfunctionornot.Partialfunctioncanbecodedwithorwithoutcase-likestatements.

Page 93: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SimplePartialfunctioninScalaREPLFollowingisasimplePartialfunctionusingScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valpositiveNumber=newPartialFunction[Int,Int]{

|defapply(n:Int)=n/n

|defisDefinedAt(n:Int)=n!=0

|}

positiveNumber:PartialFunction[Int,Int]=<function1>

scala>

scala>println(positiveNumber.isDefinedAt(6))

true

scala>println(positiveNumber.isDefinedAt(0))

false

scala>

scala>println(positiveNumber(6))

1

scala>println(positiveNumber(0))

java.lang.ArithmeticException:/byzero

at$anon$1.apply$mcII$sp(<console>:12)

...32elided

scala>

PartialfunctionsareScalaclasses.Theyhavesomemethodsyouneedtoprovide,forinstance,applyandisDefinedAt.ThefunctionisDefinedAtisusedbythecallertocheckifthePartialFunctionwillacceptandoperatewiththevaluesupplied.TheapplyfunctionwilldotheworkwhenthePartialFunctionisexecutedbyScala.

Page 94: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaPartialFunctionwithoutOOusingcasestatementsinScalaREPLFollowingisaScalaPartialFunctionwithoutOOusingcasestatementsinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valpositiveNumber:PartialFunction[Int,Int]={

|casen:Intifn!=0=>n/n

|}

positiveNumber:PartialFunction[Int,Int]=<function1>

scala>

scala>println(positiveNumber.isDefinedAt(6))

true

scala>println(positiveNumber.isDefinedAt(0))

false

scala>

scala>println(positiveNumber(6))

1

scala>println(positiveNumber(0))

scala.MatchError:0(ofclassjava.lang.Integer)

atscala.PartialFunction$$anon$1.apply(PartialFunction.scala:253)

atscala.PartialFunction$$anon$1.apply(PartialFunction.scala:251)

at$anonfun$1.applyOrElse(<console>:11)

at$anonfun$1.applyOrElse(<console>:11)

atscala.runtime.AbstractPartialFunction$mcII$sp.apply$mcII$sp

(AbstractPartialFunction.scala:36)

...32elided

scala>

ScalawasamorefluentwaytoworkwithPartialFunctionusingthecasestatements.Whenyouusethecasestatements,youdon'tneedtosupplytheapplyandisDefinedAtfunctions,sincethePatternMatchertakescareofthat.

Page 95: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PartialFunctioncompositioninScalaREPLFollowingisaPartialFunctioncompositioninScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>valeven:PartialFunction[Int,String]={

|caseiifi%2==0=>"even"

|}

even:PartialFunction[Int,String]=<function1>

scala>

scala>valodd:PartialFunction[Int,String]={case_=>"odd"}

odd:PartialFunction[Int,String]=<function1>

scala>

scala>valevenOrOdd:(Int=>String)=evenorElseodd

evenOrOdd:Int=>String=<function1>

scala>

scala>println(evenOrOdd(1)=="odd")

true

scala>println(evenOrOdd(2)=="even")

true

scala>

ScalaallowsustocomposeasmanyPartialFunctionsaswewant.PartialFunctioncompositionhappenswiththeorElsefunction.Intheprecedingcode,wedefinedanimmutablevariablecalledeven,whichverifiesevennumbers.Secondly,wecreatedasecondimmutablevariablecalledodd,whichchecksforoddnumbers.Thenwedidthecomposition,andcreatedathirdPartialFunctioncalledevenOrOddwithcomposeevenandoddusingtheorElseoperator.

Page 96: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PackageobjectsScalahaspackageslikeJava.However,Scalapackagesarealsoobjects,andyoucanhavecodeinsideapackage.JavadoesnothavethesamepowerasScalaintermsofpackages.Ifyouaddcodetoapackage,itwillbeavailabletoallclassesandfunctionswithinthatpackage.

Page 97: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

package.scalaYourpackage.scalafileshouldcontainthefollowingcode

packagecom.packait.scala.book

packageobjectcommons{

valPI=3.1415926

objectconstraintsHolder{

valODD="Odd"

valEVEN="Even"

}

defisOdd(n:Int):String=if(n%2==0)constraintsHolder.ODDelse

null

defisEven(n:Int):String=if(n%2!=0)constraintsHolder.EVEN

elsenull

defshow(s:String)=println(s)

}

ThisistheScalapackageobject.Thereisthisspecialtokencalledpackageobjectwhichyouusetodefinecommoncodetoallclasses,objects,andfunctionsthataredefinedinsidethispackageorsub-package.Forthiscase,wedefineavalueofPIasaconstantandalsooneobjectholdercontainingtheStringvaluesforOddandEven.Therearealsothreehelperfunctions,whichcanandwillbeusedbytheclassesinsidethispackage.

Page 98: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MainApp.scalaYourMainApp.scalafileshouldcontainthefollowingcode

packagecom.packait.scala.book.commons

objectMainAppextendsApp{

show("PIis:"+PI)

show(constraintsHolder.getClass.toString())

show(isOdd(2))

show(isOdd(6))

show(isEven(3))

show(isEven(7))

}

Asyoucanseeintheprecedingcode,thisnewobjectisplacedinthepackage:com.packait.scala.book.commons.Anotherinterestingthingisthefactthatwedon'thaveanyimportstatementherebecauseofthepackageobjectfeature.Whenyoucompileandrunthisprogram,youwillseethefollowingoutput:

PIis:3.1415926

classcom.packait.scala.book.commons.package$constraintsHolder$

Odd

Odd

Even

Even

ScalausesthePackageobjectagreatdealprovidinglotsofshortcutsandconvenienceforallScaladevelopers.ThefollowingistheScalapackageobjectdefinition:

/*__

*\

**___________//___ScalaAPI

**

**/__/__//_|///_|(c)2003-2013,LAMP/EPFL

**

**__\\//__/__|//__/__|http://scala-lang.org/

**

**/____/\___/_/|_/____/_/||

**

**|/

**

\*

*/

/**

*CoreScalatypes.Theyarealwaysavailablewithoutanexplicit

import.

*@contentDiagramhideNodes"scala.Serializable"

*/

Page 99: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

packageobjectscala{

typeThrowable=java.lang.Throwable

typeException=java.lang.Exception

typeError=java.lang.Error

typeRuntimeException=java.lang.RuntimeException

typeNullPointerException=

java.lang.NullPointerException

typeClassCastException=

java.lang.ClassCastException

typeIndexOutOfBoundsException=

java.lang.IndexOutOfBoundsException

typeArrayIndexOutOfBoundsException=

java.lang.ArrayIndexOutOfBoundsException

typeStringIndexOutOfBoundsException=

java.lang.StringIndexOutOfBoundsException

typeUnsupportedOperationException=

java.lang.UnsupportedOperationException

typeIllegalArgumentException=

java.lang.IllegalArgumentException

typeNoSuchElementException=

java.util.NoSuchElementException

typeNumberFormatException=

java.lang.NumberFormatException

typeAbstractMethodError=

java.lang.AbstractMethodError

typeInterruptedException=

java.lang.InterruptedException

//Adummyusedbythespecializationannotation.

valAnyRef=newSpecializable{

overridedeftoString="objectAnyRef"

}

typeTraversableOnce[+A]=scala.collection.TraversableOnce[A]

typeTraversable[+A]=scala.collection.Traversable[A]

valTraversable=scala.collection.Traversable

typeIterable[+A]=scala.collection.Iterable[A]

valIterable=scala.collection.Iterable

typeSeq[+A]=scala.collection.Seq[A]

valSeq=scala.collection.Seq

typeIndexedSeq[+A]=scala.collection.IndexedSeq[A]

valIndexedSeq=scala.collection.IndexedSeq

typeIterator[+A]=scala.collection.Iterator[A]

valIterator=scala.collection.Iterator

typeBufferedIterator[+A]=scala.collection.BufferedIterator[A]

typeList[+A]=scala.collection.immutable.List[A]

valList=scala.collection.immutable.List

Page 100: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

valNil=scala.collection.immutable.Nil

type::[A]=scala.collection.immutable.::[A]

val::=scala.collection.immutable.::

val+:=scala.collection.+:

val:+=scala.collection.:+

typeStream[+A]=scala.collection.immutable.Stream[A]

valStream=scala.collection.immutable.Stream

val#::=scala.collection.immutable.Stream.#::

typeVector[+A]=scala.collection.immutable.Vector[A]

valVector=scala.collection.immutable.Vector

typeStringBuilder=scala.collection.mutable.StringBuilder

valStringBuilder=scala.collection.mutable.StringBuilder

typeRange=scala.collection.immutable.Range

valRange=scala.collection.immutable.Range

//Numerictypeswhichweremovedintoscala.math.*

typeBigDecimal=scala.math.BigDecimal

valBigDecimal=scala.math.BigDecimal

typeBigInt=scala.math.BigInt

valBigInt=scala.math.BigInt

typeEquiv[T]=scala.math.Equiv[T]

valEquiv=scala.math.Equiv

typeFractional[T]=scala.math.Fractional[T]

valFractional=scala.math.Fractional

typeIntegral[T]=scala.math.Integral[T]

valIntegral=scala.math.Integral

typeNumeric[T]=scala.math.Numeric[T]

valNumeric=scala.math.Numeric

typeOrdered[T]=scala.math.Ordered[T]

valOrdered=scala.math.Ordered

typeOrdering[T]=scala.math.Ordering[T]

valOrdering=scala.math.Ordering

typePartialOrdering[T]=scala.math.PartialOrdering[T]

typePartiallyOrdered[T]=scala.math.PartiallyOrdered[T]

typeEither[+A,+B]=scala.util.Either[A,B]

valEither=scala.util.Either

typeLeft[+A,+B]=scala.util.Left[A,B]

valLeft=scala.util.Left

Page 101: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

typeRight[+A,+B]=scala.util.Right[A,B]

valRight=scala.util.Right

//Annotationswhichwemightmovetoannotation.*

/*

typeSerialVersionUID=annotation.SerialVersionUID

typedeprecated=annotation.deprecated

typedeprecatedName=annotation.deprecatedName

typeinline=annotation.inline

typenative=annotation.native

typenoinline=annotation.noinline

typeremote=annotation.remote

typespecialized=annotation.specialized

typetransient=annotation.transient

typethrows=annotation.throws

typeunchecked=annotation.unchecked.unchecked

typevolatile=annotation.volatile

*/

}

Page 102: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

FunctionsLikeanygreatFPlanguage,Scalahaslotsofbuilt-infunctions.Thesefunctionsmakeourcodemorefluentandfunctional;nowit'stimetolearnsomeofthesefunctions:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>//Createsthenumbers1,2,3,4,5andthemmultiplytheyby2and

createsanewVector

scala>println((1to5).map(_*2))

Vector(2,4,6,8,10)

scala>

scala>//Creates1,2,3andsumthemallwitheachorherandreturnthetotal

scala>println((1to3).reduceLeft(_+_))

6

scala>

scala>//Creates1,2,3andmultiplyeachnumberbyitselfandreturna

Vector

scala>println((1to3).map(x=>x*x))

Vector(1,4,9)

scala>

scala>//Createsnumbers1,2,3,4ans5filteronlyOddnumbersthemmultiply

themoddsby2andreturnaVector

scala>println((1to5)filter{_%2==0}map{_*2})

Vector(4,8)

scala>

scala>//CreatesaListwith1to5andthemprinteachelementbeing

multiplyedby2

scala>List(1,2,3,4,5).foreach((i:Int)=>println(i*2))

2

4

6

8

10

scala>

scala>//CreatesaListwith1to5andthenprinteachelementbeing

multipliedby2

scala>List(1,2,3,4,5).foreach(i=>println(i*2))

2

4

6

8

10

scala>

scala>//Drops3elementsfromthelists

scala>println(List(2,3,4,5,6).drop(3))

List(5,6)

scala>println(List(2,3,4,5,6)drop3)

List(5,6)

scala>

scala>//Zip2listsintoasingleone:Itwilltake1elementofeachlist

andcreateapairList

scala>println(List(1,2,3,4).zip(List(6,7,8)))

Page 103: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

List((1,6),(2,7),(3,8))

scala>

scala>//Takenestedlistsandcreateasinglelistwithflatelements

scala>println(List(List(1,2),List(3,4)).flatten)

List(1,2,3,4)

scala>

scala>//FindsapersoninaListbyAge

scala>caseclassPerson(age:Int,name:String)

definedclassPerson

scala>println(List(Person(31,"Diego"),Person(40,"Nilseu")).find((p:Person)

=>p.age<=33))

Some(Person(31,Diego))

scala>

Page 104: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PartialapplicationInScala,theunderscore(_)meansdifferentthingsindifferentcontexts.Theunderscorecanbeusedtopartiallyapplyafunction.Itmeansavaluewillbesuppliedlater.Thisfeatureisusefulforfunctioncompositionandallowsyoutoreusefunctions.Let'sseesomecode.

Page 105: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PartialfunctioninScalaREPLFollowingisanexampleusingPartialfunctioninScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>defsum(a:Int,b:Int)=a+b

sum:(a:Int,b:Int)Int

scala>

scala>valadd6=sum(6,_:Int)

add6:Int=>Int=<function1>

scala>

scala>println(add6(1))

7

scala>

Intheprecedingcode,first,wedefineafunctioncalledsum,whichtakestwoIntparametersandcalculatesasumofthesetwoparameters.Later,wedefineafunctionandholditasavariablecalledadd6.Fortheadd6functiondefinition,wejustcallthesumfunctionpassing6and_.Scalawillgettheparameterpassedthroughadd6,andpassitthroughthesumfunction.

Page 106: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CurriedfunctionsThisfeatureisverypopularinfunctionlanguageslikeHaskell.Curriedfunctionsaresimilartopartialapplications,becausetheyallowsomeargumentstopassnowandotherslater.However,theyarealittlebitdifferent.

Page 107: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Curriedfunctions-ScalaREPLFollowingisanexampleusingcurriedfunctioninScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>//FunctionDefinition

scala>defsum(x:Int)(y:Int):Int=x+y

sum:(x:Int)(y:Int)Int

scala>

scala>//Functioncall-Callingacurriedfunction

scala>sum(2)(3)

res0:Int=5

scala>

scala>//DoingpartialwithCurriedfunctions

scala>valadd3=sum(3)_

add3:Int=>Int=<function1>

scala>

scala>//Supplythelastargumentnow

scala>add3(3)

res1:Int=6

scala>

Fortheprecedingcode,wecreateacurriedfunctioninthefunctiondefinition.Scalaallowsustotransformregular/normalfunctionsintocurriedfunctions.Thefollowingcodeshowstheusageofthecurriedfunction.

Page 108: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CurriedtransformationinScalaREPLFollowingisanexampleusingcurriedtransformationinScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>defnormalSum(x:Int,y:Int):Int=x+y

normalSum:(x:Int,y:Int)Int

scala>

scala>valcurriedSum=(normalSum_).curried

curriedSum:Int=>(Int=>Int)=<function1>

scala>

scala>valadd3=curriedSum(3)

add3:Int=>Int=<function1>

scala>

scala>println(add3(3))

6

scala>

Page 109: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

OperatoroverloadingLikeC++,Scalapermitsoperatoroverload.ThisfeatureisgreatforcreatingcustomDomainSpecificLanguages(DSL),whichcanbeusefultocreatebettersoftwareabstractionsoreveninternalorexternalAPIsfordevelopers,orforbusinesspeople.Youshouldusethisfeaturewithwisdom--imagineifallframeworksdecidetooverloadthesameoperatorswithimplicits!Youmightrunintotrouble.ScalaisaveryflexiblelanguagecomparedtoJava.However,youneedtobecareful,otherwiseyoucouldcreatecodethat'shardtomaintainorevenincompatiblewithotherScalaapplications,libraries,orfunctions.

Page 110: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaoperatoroverloadinginScalaREPLFollowingisanexampleusingScalaoperatoroverloadinginScalaREPL:

$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>caseclassMyNumber(value:Int){

|def+(that:MyNumber):MyNumber=newMyNumber(that.value+this.value)

|def+(that:Int):MyNumber=newMyNumber(that+this.value)

|}

definedclassMyNumber

scala>valv=newMyNumber(5)

v:MyNumber=MyNumber(5)

scala>

scala>println(v)

MyNumber(5)

scala>println(v+v)

MyNumber(10)

scala>println(v+newMyNumber(4))

MyNumber(9)

scala>println(v+8)

MyNumber(13)

scala>

Asyoucansee,wehavetwofunctionscalled+.OneofthisfunctionsreceivesaMyNumbercaseclass,andtheotherreceivesaIntvalue.YoucanuseOOinScalawithregularclassesandfunctionsaswellifyouwish.We'realsofavoringimmutabilityherebecausewealwayscreateanewinstanceofMyNumberwhentheoperation+happens.

Page 111: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImplicitsImplicitsallowyoutodomagicinScala.Withgreatpowercomesgreatresponsibility.ImplicitsallowtoyoucreateverypowerfulDSL,buttheyalsoallowyoutogetcrazy,sodoitwithwisdom.Youareallowedtohaveimplicitfunctions,classes,andobjects.TheScalalanguageandothercoreframeworksfromtheScalaecosystemlikeAkkaandPlayFrameworkuseimplicitsmanytimes.

Page 112: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaImplicitsinSCALAREPL$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>implicitdeftransformStringtoInt(n:String)=n.toInt

warning:therewasonefeaturewarning;re-runwith-featurefordetails

transformStringtoInt:(n:String)Int

scala>

scala>vals:String="123456"

s:String=123456

scala>println(s)

123456

scala>

scala>vali:Int=s

i:Int=123456

scala>println(i)

123456

scala>

Touseimplicits,youneedtousethekeywordimplicitbeforeafunction.Scalawillimplicitlycallthatfunctionwhenitisappropriate.Forthiscase,itwillcalltoconverttheStringtypetoInttypeaswecansee.

Page 113: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImplicitParameteratScalaREPL$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>implicitvalyValue:Int=6

yValue:Int=6

scala>defsum(x:Int)(implicityValue:Int)=x+yValue

sum:(x:Int)(implicityValue:Int)Int

scala>valresult=sum(10)

result:Int=16

scala>println(result)

16

scala>

Forthisothercase,giveninthelastcode,weuseanimplicitparameterinthefunctionsum.Wealsousedacurriedfunctionhere.Wedefinedtheimplicitfunctionfirst,andthencalledthesumfunction.Thistechniqueisgoodforexternalizedfunctionsconfigurationandvaluesyouwouldletithardcode.Italsosaveslinesofcode,becauseyoudon'tneedtopassaparametertoallfunctionsallthetime,soit'squitehandy.

Page 114: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

FuturesFuturesenableanefficientwaytowriteparalleloperationsinanonblockingIOfashion.Futuresareplaceholderobjectsforvaluesthatmightnotexistyet.Futuresarecomposable,andtheyworkwithcallbacksinsteadoftraditionalblockingcode.

Page 115: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SimpleFuturecodeinScalaREPL$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>importconcurrent.Future

importconcurrent.Future

scala>importconcurrent.ExecutionContext.Implicits.global

importconcurrent.ExecutionContext.Implicits.global

scala>

scala>valf:Future[String]=Future{"Helloworld!"}

f:scala.concurrent.Future[String]=Success(Helloworld!)

scala>

scala>println("Result:"+f.value.get.get)

Result:Helloworld!

scala>

scala>println("Result:"+f)

Result:Success(Helloworld!)

scala>

InordertoworkwithfuturesinScala,wehavetoimportconcurrent.Future.Wealsoneedanexecutor,whichisawaytoworkwiththreads.Scalahasadefaultsetofexecutionservices.Youcantweakitifyoulike,however,fornowwecanjustusethedefaults;todothat,wejustimportconcurrent.ExecutionContext.Implicits.global.

It'spossibletoretrievetheFuturevalue.ScalahasaveryexplicitAPI,whichmakesthedeveloper'slifeeasier,andalsogivesgoodsamplesforhowweshouldcodeourownAPIs.Futurehasamethodcalledvalue,whichreturnsOption[scala.util.Try[A]]whereAisthegenerictypeyouareusingforthefuture;forourcase,it'saStringA.Tryisadifferentwaytodoatry...catch,andthisissafer,becausethecallerknowsbeforehandthatthecodetheyarecallingmayfail.Try[Optional]meansthatScalawilltrytorunsomecodeandthecodemayfail--evenifitdoesnotfail,youmightreceiveNoneorSome.Thistypeofsystemmakeseverybody'slivesbetter,becauseyoucanhaveSomeorNoneastheOptionreturn.Futuresareakindofcallback.Forourprevioussamplecode,theresultwasobtainedquitequickly,however,weoftenusefuturestocallexternalAPIs,RESTservices,Microservices,SOAPWebservices,oranycodethattakestimetorunandmightnotgetcompleted.FuturesalsoworkwithPatternMatcher.Let'sseeanothersamplecode.

Page 116: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AcompleteFuturesampleatScalaREPL$scala

WelcometoScala2.11.8(JavaHotSpot(TM)64-BitServerVM,Java1.8.0_77).

Typeinexpressionsforevaluation.Ortry:help.

scala>importconcurrent.Future

importconcurrent.Future

scala>importconcurrent.ExecutionContext.Implicits.global

importconcurrent.ExecutionContext.Implicits.global

scala>importscala.util.{Success,Failure}

importscala.util.{Success,Failure}

scala>defcreateFuture():Future[Int]={

|Future{

|valr=scala.util.Random

|if(r.nextInt(100)%2==0)0elsethrownewRuntimeException("ODDnumbers

arenotgoodhere:(")

|}

|}

createFuture:()scala.concurrent.Future[Int]

scala>defevaluateFuture(f:Future[_]){

|f.onComplete{

|caseSuccess(i)=>println(s"ASuccess$i")

|caseFailure(e)=>println(s"Somethingwentwrong.Ex:

${e.getMessage}")

|}

|}

evaluateFuture:(f:scala.concurrent.Future[_])Unit

scala>evaluateFuture(createFuture)

scala>Somethingwentwrong.Ex:ODDnumbersarenotgoodhere:(

evaluateFuture(createFuture)

ASuccess0

scala>evaluateFuture(createFuture)

Somethingwentwrong.Ex:ODDnumbersarenotgoodhere:(

scala>evaluateFuture(createFuture)

Somethingwentwrong.Ex:ODDnumbersarenotgoodhere:(

scala>evaluateFuture(createFuture)

ASuccess0

scala>evaluateFuture(createFuture)

ASuccess0

scala>

ThereisafunctioncalledcreateFuture,whichcreatesFuture[Int]eachtimeyoucallit.Intheprecedingcode,weusescala.util.Randomtogeneraterandomnumbersbetween0and99.Ifthenumberiseven,wereturna0,whichmeanssuccess.However,ifthenumberisodd,wereturnaRuntimeException,whichwillmeanafailure.

ThereisasecondfunctioncalledevaluateFuture,whichreceivesanyFuture.Weallowaresultofanykindofgenericparameterizedtypeoffunction,becauseweusedthemagicunderscore_.ThenweapplyPatternMatcherwithtwocaseclasses:SuccessandFailure.Inboththecases,wejustprintonstdin.WealsouseanotherinterestingandhandyScalafeaturecalledStringinterpolation.WeneedtowestarttheStringwithsbefore"".Thisallowsustouseexpressionswith$and${}toevaluateanyvariableinthecontext.Thisisadifferent

Page 117: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

approachforStringconcatenationfromwhatwehavedonesofar.Later,wemade6callsfortheevaluteFuturefunction,passinganewFutureeachtime,createdbythefunctioncreateFuture.

Page 118: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReactiveProgramingandRxScalaReactiveprogrammingisbetter,scalable,andafasterwaytobuildapplications.ReactiveProgramingcanbedonewithOOlanguages,however,theymakealotofsensewithFPlanguages.WhenFPismarriedtoReactivePrograming,wegetsomethingcalledFunctionalReactivePrograming(FRP).ScalaFRPcanbeusedformanypurposeslikeGUI,Robotics,andMusic,becauseitgivesyouabettermodeltomodeltime.Reactiveprogrammingisanewtechnique,whichworkswithStreams(alsoknownasDataFlows).Streamsisawaytothinkandcodeapplicationsinawaywhichcanexpressdatatransformationsandflow.Themainideaistopropagatechangesthroughacircuitorflow.Yes,wearetalkingaboutanewwaytodoasyncprogramming.

ThemainlibraryforReactiveProgramingiscalledReactiveExtensions(Rx)-http://reactivex.io/),originallybuiltfor.NETbyEricMeijer.ItcombinesthebestideasfromtheObserverandIteratorPatterns,andFP.RxhasimplementationsformanylanguageslikeScala,Java,Python,.NET,PHP,andothers(https://github.com/ReactiveX).CodingwithRxiseasy,andyoucancreateStreams,combinewithquery-likeoperators,andalsolisten(subscribe)toanyobservableStreamstoperformdatatransformations.RxisusedbymanysuccessfulcompaniestodaylikeNetflix,GitHub,Microsoft,SoundCloud,Couchbase,Airbnb,Trello,andseveralothers.Inthisbook,wewilluseRxScala,whichistheScalaimplementationoftheReactiveStreams.

Thefollowingtableshowsthemainclass/conceptsyouneedtoknowinordertoworkwithRx.

Term/Class Concept

Observable CreateasynccomposableStreamsfromsources.

Observer Acallbackfunctiontype.

Subscription TheboundbetweentheSubscriberandtheObservable.ReceivesnotificationsfromObservables.

ReactiveStreamsisalsothenameofacommonspecificationtryingtoconsolidateandstandardizethereactivestreamprocessing,ThereareseveralimplementationssuchasRxJava/RxScala,Reactor,Akka,Slick,andVert.x.Youcanfindmoreathttps://github.com/reactive-streams/reactive-streams-jvm.

BacktotheObservables--wecanperformallkindsofoperationswithobservables.For

Page 119: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

instance,wecanfilter,select,aggregate,compose,performtime-basedoperations,andapplybackpressure.TherearetwobigwinswithObservablesinsteadofcallbacks.Firstofall,Observablesarenotopinionatedabouthowlow-levelI/Oandthreadinghappens,andsecondly,whenyouaredoingcomplexcode,callbackstendtobenested,andthatiswhenthingsgetuglyandhardtoread.ObservableshaveasimplewaytodocompositionthankstoFP.

Observablespushvaluestoconsumerswhenevervaluesareavailable,whichisgreatbecausethenthevaluescanarriveinsyncorasyncfashion.Rxprovidesaseriesofcollectionoperatorstodoallsortsofdatatransformationsyoumayneed.Let'sseesomecodenow.WewilluseRxScalaversion0.26.1,whichiscompatiblewithRxJavaversion1.1.1+.RxScalaisjustawrapperforRxJava(CreatedbyNetflix).WhynotuseRxJavastraight?Becausethesyntaxwon'tbepleasant;withRxScala,wecanhaveafluentScalaexperience.RxJavaisgreat,however,Javasyntaxforthisisnotpleasant-asScalais,infact,prettyugly.

Page 120: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SimpleObservablesScalawithRxScalapackagescalabook.rx.chap1

importrx.lang.scala.Observable

importscala.concurrent.duration._

objectSimpleRXextendsApp{

valo=Observable.

interval(100millis).

take(5)

o.subscribe(x=>println(s"Gotit:$x"))

Thread.sleep(1000)

Observable.

just(1,2,3,4).

reduce(_+_).

subscribe(r=>println(s"Sum1,2,3,4is$rinaRxWay"))

}

IfyourunthisprecedingScalaprogram,youwillseethefollowingoutput:

Page 121: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SimpleObservablesScalawithRxScala-ExecutionintheconsoleGotit:0

Gotit:1

Gotit:2

Gotit:3

Gotit:4

Sum1,2,3,4is10inaRxWay

IfyoutrytorunthiscodeintheScalaREPL,itwillfail,becauseweneedtheRxScalaandRxJavadependencies.Forthis,wewillneedSBTanddependencymanagement.Donotworry,wewillcoverhowtoworkwithSBTinourScalaapplicationinthenextchapter.

Goingbacktotheobservables,weneedtoimporttheScalaObservable.MakesureyougetitfromtheScalapackage,becauseifyougettheJavaone,youwillhaveissues:intheveryfirstpartofthecode,wewillgetnumbersstartingfrom0each100milliseconds,andthiscodewouldrunforever.Toavoidthis,weusethetakefunctiontoputalimitintothecollection,sowewillgetthefirstfivevalues.Then,later,wesubscribetotheobserver,andwhendataisready,ourcodewillrun.Forthefirstsample,it'sprettyeasy,wearejustprintingthevalueswehavegot.Thereisathreadsleepinthisprogram,otherwise,theprogramwouldterminate,andyouwouldnotseeanyvalueontheconsole.

Thesecondpartofthecodedoessomethingmoreinteresting.Firstofall,itcreatesanObservablefromastaticlistofvalues,whichare1,2,3,and4.Weapplyareducefunctionintotheelements,whichwillsumalltheelementswitheachother,andthenwesubscribeandprinttheresult.

Page 122: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ComplexScalawithRxScalaObservablespackagescalabook.rx.chap1

importrx.lang.scala.Observable

objectComplexRxScalaextendsApp{

Observable.

just(1,2,3,4,5,6,7,8,9,10).//1,2,3,4,5,6,7,8,9,10

filter(x=>x%2==0).//2,4,6,8,10

take(2).//2,4

reduce(_+_).//6

subscribe(r=>println(s"#1$r"))

valo1=Observable.

just(1,2,3,4,5,6,7,8,9,10).//1,2,3,4,5,6,7,8,9,10

filter(x=>x%2==0).//2,4,6,8,10

take(3).//2,4,6

map(n=>n*n)//4,16,36

valo2=Observable.

just(1,2,3,4,5,6,7,8,9,10).//1,2,3,4,5,6,7,8,9,10

filter(x=>x%2!=0).//1,3,5,7,9

take(3).//1,3,5

map(n=>n*n)//1,9,25

valo3=o1.

merge(o2).//2,16,36,1,9,25

subscribe(r=>println(s"#2$r"))

}

TheprecedingfirstpartofthecodecreatesanObservablewithnumbersfrom1to10,andthenappliesafilterfunction,whichwillgetonlytheevennumbers.Itthenreducesthem,calculatestheirsum,andlastly,printsthesolution.Youcanvisualizeitasdepictedinthefollowingimage:

Page 123: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Forthesecondpartofthecode,wecreatetwodifferentobservables.Thefirstoneiswithevennumbersandthesecondoneiswithoddnumbers.Thesetwoobservablesaredecoupledfromeachother;youcancontrolasmanyobservablesyouwant.Lateron,thecodeusesamergefunctiontojointhesetwoobservablesintoathirdandnewobservablecontainingthecontentofthefirstandsecondobservables.

Page 124: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Merging2Observables

Therearemanyfunctionsandoptions,andyoucanseethewholelistathttp://rxmarbles.com/andhttps://github.com/ReactiveX/RxScala.Forthesakeofsimplicity,fornow,wearejustworkingwithnumbers.Later,wewillusethistodomoreadvancecompositionsincludingdatabasecallsandexternalwebservicescalls.

Page 125: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,welearnedthebasicconceptsofFP,ReactivePrograming,andtheScalalanguage.WelearnedaboutthebasicconstructsoftheScalalanguageandFunctionalProgramming,functions,collections,andOOinScala,andconcurrentprogrammingwithFutures.

Next,wewillseehowtouseSBTtobuildScalaprojects.WewilllearnhowtocompileandrunScalaapplicationsusingSBT.

Page 126: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter2.CreatingYourAppArchitectureandBootstrappingwithSBTInthepreviouschapter,welearnedaboutFunctionalProgramingandScala.ThischapterwillbefocusedonSimpleBuildTool(SBT)andActivatorinordertoBootstrapcomplexScalaandPlayframeworkprojects.UsingSBTandActivator,wecanperformseveraldevelopmenttaskssuchasbuilding,runningtests,anddeployingtheapplication(whichwillbecoveredindetailinChapter10,ScalingUp).Let'sgetstarted.

Inthischapter,wewillseethefollowingtopics:

SBTbasics--installation,structure,anddependenciesActivatorbasics--creatingprojectsOverallarchitectureofourapplication

Page 127: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntroducingSBTSBTistheultimateScalasolutionforbuildingandpackingScalaapplications.SBThaslotsofplugins,suchasEclipseandIntelliJIDEAprojectsgeneration,whichhelpagreatdealwhenwearedoingScaladevelopment.SBTisbuiltinScalainordertohelpyoubuildyourScalaapplications.However,SBTcanstillbeusedtobuildJavaapplicationsifyouwish.

ThecorefeaturesofSBTareasfollows:

Scala-basedbuilddefinitionIncrementalcompilationContinuouscompilationandtestingGreatsupportfortestinglibrariessuchasScalaCheck,Specs,ScalaTest,andJUnitREPLintegrationParallelTaskexecution

WewilluseSBTwiththeTypesafeActivatortoBootstrapourapplicationlaterinthisverychapter.Beforedoingso,wewillplaywithSBTtolearnthekeyconceptsofsettingupabuildprojectforaScalaapplication.Inthisbook,wewillbeusingSBTversion0.13.11.

Page 128: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

InstallingSBTonUbuntuLinuxKeepinmindthatweneedtohaveJavaandScalainstalledbeforeinstallingSBT.Ifyoudon'thaveJavaandScalainstalled,gobacktoChapter1,IntroductiontoFP,Reactive,andScalaandfollowtheinstallationinstructions.Openaterminalwindow,andrunthefollowingcommandsinordertodownloadandinstallSBT:

$cd/tmp

$wgethttps://repo.typesafe.com/typesafe/ivy-releases/org.scala-

sbt/sbt-launch/0.13.11/sbt-launch.jar?

_ga=1.44294116.1153786209.1462636319-Osbt-launch.jar

$chmod+xsbt-launch.jar

$mkdir~/bin/&&mkdir~/bin/sbt/

$mvsbt-launch.jar~/bin/sbt/

$cd~/bin/sbt/

$touchsbt

Addthefollowingcontenttothe~/bin/sbt/sbtfile:

#!/bin/bash

w

Aftersavingthe~/bin/sbt/sbtfile,weneedtogivepermissiontoexecutethefilewiththefollowingcommand:

$chmodu+x~/bin/sbt/sbt

NowweneedtoputSBTintotheoperationalsystempathinordertobeabletoexecuteanywhereintheLinuxterminal.WeneedtoexportSBTthroughthePATHcommandintothe~/.bashrcfile.Openthe~/.bashrcfileinyourfavoriteeditor,andaddthefollowingcontent:

exportSBT_HOME=~/bin/sbt/

exportPATH=$PATH:$SBT_HOME

Weneedtosourcethefileusing$source~/.bashrc.

NowwecanrunSBTandmoveonwiththeinstallation.Whenyounowtype$sbtonyourconsole,SBTwilldownloadallthedependenciesrequiredforusetorunitself.

Page 129: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GettingstartedwithSBTLet'screatefoldernamedhello-world-sbt,andaddthefollowingprojectstructure:

Forbuild.properties,youneedtohavethefollowingcontent:

build.properties

sbt.version=0.13.11

Forhello_world.scala,wewillusethefollowingcode:

hello_world.scala

objectSbtScalaMainAppextendsApp{

println("HelloworldSBT/ScalaApp")

}

FornowwewilluseanSBTDSL.However,sinceSBTiswrittenScala,wecanusethebuild.scalaformatifwewish.Thisishandyinsomecases,becausewecanuseanykindofScalacodeinordertomakethebuildmoredynamicandtoreusecodeandtasks.

Wewillsetsomepredefinedvariables,however,youcancreateyourownvariables,whichcanbeusedtoavoidduplicatecode.Finally,let'sseethebuild.sbtfilecontentasfollows:

build.scala

name:="hello-world-sbt"

version:="1.0"

scalaVersion:="2.11.8"

scalaVersioninThisBuild:="2.11.8"

Intheprecedingcode,wehavethenameoftheapplication,theversionwhichwillbeusedinthegeneratedJARfile,andalsotheScalaversionusedontheapplicationandtheoneusedinthebuildprocess.Wearereadytobuildthisproject,soopenyourterminalandtype$sbtcompile.

Page 130: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThisinstructionwillmakeSBTcompileourScalacode,andyoushouldseesomethinglikethisfollowingscreen:

$sbtcompile

Congratulations!SBTjustcompiledourScalaapplication.NowwecanruntheapplicationusingSBT.Inordertodothis,wejustneedtotype$sbtrunasfollows:

$sbtrun

SBTmakesiteasiertotestandplaywithyourScalaapplication,becauseSBThasaREPLliketheScalaREPLwewereplayingwithinChapter1,IntroductiontoFP,Reactive,andScala.TheSBTREPLmakesalltheScalacodethatyoumighthaveundertheprojectavailableattheREPL.

Executethecommand$sbtconsole.

Page 131: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

OnceyouareintotheREPL,youcantypeanyScalacode.Asyoumust'verealized,IjustcalledthemainScalaapplicationdirectlyvia$SbtScalaMainApp.main(null).

Page 132: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingdependenciesLikeanybuildtool,SBTallowsyoutoresolvedependencies.SBTusestheIvy/Maven2patternstoresolvedependencies.So,ifyouarefamiliarwithMaven2,Gradle,orAnt/Ivy,youwillrealizethatsettingSBTdependenciesisthesame,althoughwithadifferentsyntax.Dependenciesaredefinedinthebuild.sbtfile.ThereisnoScaladevelopmentwithoutunittests.OneofthemostpopulartestinglibrariesisJUnit(http://junit.org/junit4/).JUnitworkswithJavaandScalaprojects.SBTwilldownloadandaddJUnittoyourScalaapplicationclasspathparameter.Weneedtoeditthebuild.sbtfiletoaddJUnitasadependencyasfollows:

build.sbt

name:="hello-world-sbt"

version:="1.0"

scalaVersion:="2.11.7"

scalaVersioninThisBuild:="2.11.7"

libraryDependencies+="junit"%"junit"%"4.12"%Test

libraryDependencies+="com.novocode"%"junit-interface"%"0.11"

%"test"

testOptions+=Tests.Argument(TestFrameworks.JUnit,"-q","-v")

AsImentionedbefore,SBTusesthesamepatternasMaven2/Ivywith:groupID+artifactid+version.Ifyoudon'tknowthepatternforthelibraryyouwanttoadd,youcancheckouttheMavenRepositorywebsite(theygenerateSBTconfigsaswell)atthefollowinglink:http://mvnrepository.com/artifact/junit/junit/4.12.

Page 133: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SBThasscopefordependencies.Wedon'twanttoshipJUnitaspartofthesourcecodedependency.That'swhywehavethe%Testafterthedependencydefinition.

Onceyouhavesavedthefilewiththenewcontent,youcanrun$sbtcompile.SBTwilldownloadJUnitforyouandstorethejarintothelocalIvyrepofileslocatedat/home/YOUR_USER/.ivy2/cache.Withthedependencyinplace,wecanaddmorecodeandalsouseSBTtorunourtestsasfollows:

src/main/scala/calc.scala

classCalculator{

defsum(a:Int,b:Int):Int={

returna+b

}

defmultiply(a:Int,b:Int):Int={

returna*b

}

}

Intheprecedingcode,wejustcreatedasimpleandstraightforwardcalculatorinScala,whichcanaddtwointegernumbersandalsoperformmultiplicationoftwointegersnumbers.NowwecanmoveontotheunittestsusingJUnit.Testsneedtobelocatedinthesrc/test/scala/folder.Lookatthefollowingcode:

src/test/scala/calcTest.scala

importorg.junit.Test

Page 134: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

importorg.junit.Assert._

classCalcTest{

@Test

deftestSumOK():Unit={

valc:Calculator=newCalculator()

valresult:Int=c.sum(1,5)

assertNotNull(c)

assertEquals(6,result)

}

@Test

deftestSum0():Unit={

valc:Calculator=newCalculator()

valresult:Int=c.sum(0,0)

assertNotNull(c)

assertEquals(0,result)

}

@Test

deftestMultiplyOk():Unit={

valc:Calculator=newCalculator()

valresult:Int=c.multiply(2,3)

assertNotNull(c)

assertEquals(6,result)

}

@Test

deftestMultiply0():Unit={

valc:Calculator=newCalculator()

valresult:Int=c.multiply(5,0)

assertNotNull(c)

assertEquals(4,result)

}

}

Okay,nowwecanjustrunthetestswiththecommand$sbttestasfollows:

Asyoucanseeinthepreviousscreenshot,allthetestsarerunning.AtestiscreatedwhenweaddtheJavaannotation@Test,anditneedstobeapublicfunctionaswell.Thereisonetest,

Page 135: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

calledtestMultiply0,whichfails,becauseitexpectstheresult4,but5multipliedby0iszero,sothetestiswrong.Let'sfixthismethodbychangingassertiontoacceptzero,likeinthefollowingcode,andrerunthe$sbttestasfollows:

@Test

deftestMultiply0():Unit={

valc:Calculator=newCalculator()

valresult:Int=c.multiply(5,0)

assertNotNull(c)

assertEquals(0,result)

}

$sbttestgivesyouthefollowingresult:

Hooray!Allthetestspassed.Bydefault,SBTrunsallyourtestsinparallel,whichisgreatforspeedingupbuildtime-nobodylikestowaitwhendoingbuilds,andScalaisnotthefastesttechtobuild.However,youcandisableparalleltestsifyouwantbyaddingthefollowinglineintothebuild.sbt:

parallelExecutioninTest:=false

Page 136: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GeneratingEclipseprojectfilesfromSBTSBTviapluginscangenerateEclipsefiles.It'spossibletoaddthesepluginsdirectlyintoyourbuild.sbtfile.However,thereisabettersolution.Youcandefineglobalconfigurations,whichareideal,becauseyoudon'tneedtoaddineverysimplebuild.sbtfileyouhave.Thisalsomakesalotofsenseifyouareworkingwithmultipleprojectsand/oryouareworkingwithopensourceprojectsbecause,asitisamatterofpreference,peopleoftendonotversionateIDEfiles.

Gotothefollowingdirectoryifitexists,otherwisepleasecreatethefollowingdirectory:/home/YOUR_USER/.sbt/0.13/plugins.

Nowcreatethefilebuild.sbtwiththefollowingcontent:

/home/YOUR_USER/.sbt/0.13/plugins/build.sbtGlobalconfigfile

resolvers+=Classpaths.typesafeResolver

addSbtPlugin("com.typesafe.sbteclipse"%"sbteclipse-plugin"%

"4.0.0")

Onceyousavethefilewiththiscontent,wecanreloadourSBTapplicationbyexecuting$sbtreload,orquittheSBTconsole(Ctrl+D)andopensbtagainusing$sbt.

$sbtreload

NowwecangenerateEclipsefilesbyusingthecommand$eclipse.

Page 137: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Oncethegenerationisdone,youcanimportthe.projectfilegeneratedintoEclipse.

Bydefault,EclipsedoesnotattachsourcefolderswhengeneratingtheEclipseproject.Ifyouwantthesourcecode(ofthird-partydepslikeJunit),youneedtoaddanextralineintoyourbuild.sbtproject.Addingsourcefoldersisoftenagoodidea,otherwise,youcan'tdoproperdebuggingwithoutthesourcecode.

build.sbt

EclipseKeys.withSource:=true

TheSBTScalaapplicationimportedintoEclipseisshowninthefollowingscreenshot:

Page 138: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ApplicationdistributionForthissection,wewillplaywiththreedifferentpackagingsolutions,whichareasfollows:

ThedefaultSBTpackagersSBTassemblypluginSBTnativepackager

SBTcangeneratejarsbydefault.ItisalsopossibletogenerateRPMs,DEBs,andevendockerimagesviaSBTplugins.Firstofall,let'sgenerateanexecutablejar.ThisisdonebythetaskpackageinSBT.OpenyourSBTconsole,andrunthe$sbtpackage.However,wewanttogenerateaFATjar,whichisajarwithallotherdependencies(jars)oftheapplication.Inordertodothat,weneedtouseanotherplugincalledassembly.

TheSBTpackagecangenerateajar,butitdoesnotshipthedependencies.Inordertousetheassemblyplugin,createthefileproject/assembly.sbt,andaddthecontentasfollows:

$project/assembly.sbt

addSbtPlugin("com.eed3si9n"%"sbt-assembly"%"0.11.2")

Inourbuild.sbt,weneedtoimporttheassemblyplugin,likethis:

$build.sbt(putintothetopofthefile)

importAssemblyKeys._

assemblySettings

Nowwecanrun$sbtassemblytogenerateourFATjar.

Page 139: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Therewego.NowwecanrunthisasanormalJavaapplicationjustusingthecommandjava-jarasfollows:

$java-jarhello-world-sbt/target/scala-2.11/hello-world-sbt-

assembly-1.0.jar

Page 140: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

HelloworldSBT/ScalaAppThereisanotherusefulpluginforpackingtheScalaapplication,whichissbt-native-packager.sbt-native-packagercangeneratepackagesforLinuxOSlikeDEBandRPMfiles.Sincethisisanewplugin,weneedtocreateafilecalledplugins.sbtinproject/asfollows:

resolvers+="Typesaferepository"at

"http://repo.typesafe.com/typesafe/releases/"

addSbtPlugin("com.typesafe.sbt"%%"sbt-native-packager"%"1.0.4")

Attheveryendofyourbuild.sbt,youneedtoaddthisline:

enablePlugins(JavaAppPackaging)

Nowwecangeneratepackageswithsbt-native-packagerusing$sbtuniversal:packageBinor$sbtuniversal:packageZipTarball.

NowwehaveaZIPandaTGZfilewithyourapplicationinthefolderhello-world-sbt/target/universal/.InsidethisZIP/TGZfile,wehaveourapplicationinajarformatwithallthedependencies;fornowwejusthaveScala,butifwehadmore,theywouldbethereaswell.ThereareSHandBATscriptstorunthisapplicationeasilyinLinux(SH)andWindows(BAT)respectively.

sbt-native-packagercanalsocookdockerimages.Thisisgreat,becausethatmakesiteasiertodeployapplicationsintoproductionenvironments.Ourprojectisfullyreadytobakedockerimages.WeneedtohavedockerinstalledonLinux;youcandosobyrunningthefollowingcommands:

sudoapt-getupdate

sudoapt-getinstalldocker-engine

sudoservicedockerstart

Page 141: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

sudodockerrunhello-world

Youshouldseesomethinglikethefollowingscreenshotifyou'vesuccessfullyinstalledDocker:

Nowyoucanrun$sbt,andthengenerateyourdockerimagesbyusingthecommand$docker:publishLocal.Youwillseeanoutputsimilartothefollowing:

>docker:publishLocal

[info]Wrote

/home/diego/github/diegopacheco/Book_Building_Reactive_Functional_Scala_Applic

ations/hello-world-sbt/target/scala-2.11/hello-world-sbt_2.11-1.0.pom

[info]SendingbuildcontexttoDockerdaemon5.769MB

[info]Step1:FROMjava:latest

[info]Pullingrepositorydocker.io/library/java

[info]31ae46664586:Pullingimage(latest)fromdocker.io/library/java

[info]31ae46664586:Pullingimage(latest)fromdocker.io/library/java,

endpoint:https://registry-1.docker.io/v1/

[info]31ae46664586:Pullingdependentlayers

[info]e9fa146e2b2b:Pullingmetadata

[info]Status:Downloadednewerimageforjava:latest

[info]docker.io/library/java:thisimagewaspulledfromalegacyregistry.

Important:Thisregistryversionwillnotbesupportedinfutureversionsof

docker.

[info]--->31ae46664586

[info]Step2:WORKDIR/opt/docker

[info]--->Runningin74c3e354e9fd

[info]--->d67542bcaa1c

Page 142: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

[info]Removingintermediatecontainer74c3e354e9fd

[info]Step3:ADDopt/opt

[info]--->f6cec2a2779f

[info]Removingintermediatecontainer0180e167ae2d

[info]Step4:RUNchown-Rdaemon:daemon.

[info]--->Runningin837ecff2ffcc

[info]--->8a261bd9d88a

[info]Removingintermediatecontainer837ecff2ffcc

[info]Step5:USERdaemon

[info]--->Runningin6101bd5b482b

[info]--->e03f5fa23bdf

[info]Removingintermediatecontainer6101bd5b482b

[info]Step6:ENTRYPOINTbin/hello-world-sbt

[info]--->Runningin43de9335129c

[info]--->eb3961f1e26b

[info]Removingintermediatecontainer43de9335129c

[info]Step7:CMD

[info]--->Runningin302e1fcd0a3d

[info]--->04e7872e85fa

[info]Removingintermediatecontainer302e1fcd0a3d

[info]Successfullybuilt04e7872e85fa

[info]Builtimagehello-world-sbt:1.0

[success]Totaltime:447s,completed07/05/201617:41:47

>

Youcanconfirmthatthereisanewdockerimageinyoursystemjustbyrunningthecommand$dockerps:

Theveryfirstimageisourdockerimagegeneratedbythesbt-native-packagerpluginwithourScalaapplication.Congratulations!YouhaveadockercontainerrunningwithyourScalaapplication.SBTNativePackagerisreallypowerful,yetsimpletouse.Youcangetmoredetailsontheofficialdocumentationsite(http://www.scala-sbt.org/sbt-native-packager/gettingstarted.html).

ThesearethebasicthingsweneedtoknowaboutSBTtobuildprofessionalScalaapplications.SBThasmanyotherfeaturesandpossibilities,whichyoucancheckitoutathttp://www.scala-sbt.org/0.13/docs/index.html.Next,wewilllearnaboutTypesafeActivator,whichisawrapperaroundSBTthatmakesiteasytousewithPlayframeworkapplications.

Page 143: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BootstrappingourPlayframeworkappwithActivatorLightband(formerTypesafe)hasanothertoolcalledActivator(https://www.lightbend.com/community/core-tools/activator-and-sbt),whichisawrapperontopofSBT.ActivatormakesiteasiertocreateReactiveapplicationsusingScala,Akka,andthePlayframework.Don'tworryaboutthePlayframeworkrightnow,becausewewillcoverthatingreaterdetailinChapter3,DevelopingtheUIwithPlayFramework.AkkawillbecoveredindetailinChapter8,DevelopingachatwithAkka.

Let'sdownloadandinstallActivator,andBootstrapourarchitecture.Remember,weneedtohaveJava8andScala2.11alreadyinstalled.Ifyoudon'thaveJava8orScala2.11,gobacktoChapter1,IntroductiontoFP,Reactive,andScalaandinstallthem.

Firstofall,youneedtodownloadactivatorfromhere:https://www.lightbend.com/activator/download

Irecommendthatyoudownloadtheminimalpackage,andletActivatordownloadandinstalltherestoftheotherdependenciesforyou.Youcandownloadtheminimalpackagehere:https://downloads.typesafe.com/typesafe-activator/1.3.10/typesafe-activator-1.3.10-minimal.zip.

Forthisbook,wewillbeusingversion1.3.10.Weneedtoputtheactivator/binfolderintheOSPATH.Ifyouwant,youcaninstallActivatorusingtheterminal,likethis:

Ifyouwant,youcaninstallActivatorusingtheterminal,likethis:

$cd/usr/local/

$wgethttps://downloads.typesafe.com/typesafe-

activator/1.3.10/typesafe-activator-1.3.10-minimal.zip

$tar-xzftypesafe-activator-1.3.10-minimal.zip

$rm-rftypesafe-activator-1.3.10-minimal.zip

$sudoecho'exportPATH=$PATH:/usr/local/typesafe-activator-

1.3.10-minimal/bin'>>~/.bashrc

$source>>~/.bashrc

Inordertotestyourinstallation,executethiscommand:

$activatornewReactiveWebStore

TheprecedingcommandwillBootstrapanarchitectureforyouwithScala,Akka,Playframework,andSBT.

Activatorwillaskyouaseriesofquestionslikesuchaswhattemplatesyoumightliketouse.ThereareacoupleoftemplatesforJavaapplications,Scalaapplications,Akkaapplications,andPlayapplications.Fornow,wewillpickoption6)play-scala.

Page 144: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThefirsttimeyourunActivator,itcouldtakesometime,becauseitwilldownloadallthedependenciesfromtheweb.WhenActivatorfinishes,youshouldseeafoldercalledReactiveWebStoreinyourfilesystem.

Thecommand$activatornewReactiveWebStoreshowsthefollowingresult:

YoushouldentertheReactiveWebStorefolderifyoutype$llintotheconsole,andyoushouldalsoseethefollowingstructure:

diego@4winds:~/bin/activator-1.3.10-minimal/bin/ReactiveWebStore$

ll

total52

drwxrwxr-x9diegodiego4096Mai1419:03./

drwxr-xr-x3diegodiego4096Mai1419:03../

drwxrwxr-x6diegodiego4096Mai1419:03app/

drwxrwxr-x2diegodiego4096Mai1419:03bin/

-rw-rw-r--1diegodiego346Mai1419:03build.sbt

drwxrwxr-x2diegodiego4096Mai1419:03conf/

-rw-rw-r--1diegodiego80Mai1419:03.gitignore

drwxrwxr-x2diegodiego4096Mai1419:03libexec/

-rw-rw-r--1diegodiego591Mai1419:03LICENSE

drwxrwxr-x2diegodiego4096Mai1419:03project/

drwxrwxr-x5diegodiego4096Mai1419:03public/

-rw-rw-r--1diegodiego1063Mai1419:03README

Page 145: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

drwxrwxr-x2diegodiego4096Mai1419:03test/

Thecontentisexplainedasfollows:

app:ThisisthePlayframeworkapplicationfolderwherewewilldotheScalawebdevelopment.build.sbt:Thisisthebuildfile;asyoucansee,ActivatorhasgeneratedtheSBTbuildconfigforus.conf:ThisholdstheapplicationconfigfilessuchasloggingandScala/Playappconfig.project:ThisistheSBTprojectfolderwherewedefineSBTpluginsandSBTversion.test:Thisholdsthetestsourcecodeforourapplication.public:ThisholdsstaticHTMLassetslikeImages,CSS,andJavaScriptcode.bin:ThisholdsacopyoftheactivatorscriptforLinux/MacandWindows.libexec:ThisholdstheActivatorjar.Thisisprettyuseful,becauseActivatorhaspackeditselfwithourapplication.So,let'ssayyoupushthisapplicationforGitHub-whensomeoneneedstoaccessthisappanddownloaditfromGitHub,theSBTfilewillbethere,sotheywon'tneedtodownloaditfromtheInternet.Thisisespeciallyusefulwhenyouareprovisioninganddeployingapplicationsinproduction,whichthisbookwillcoverindetailinChapter10,ScalingUp.

Page 146: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ActivatorshellActivatorallowsyoutorunREPLlikewedidinScalaandSBT.InordertogetREPLaccess,youneedtotypethefollowingontheconsole:

$activatorshell

Activatorhasplentyoftasksthatyoucanuse.Inordertoknowalltheavailablecommands,youcantype$activatorhelpontheconsole.

Page 147: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About
Page 148: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Activator-compiling,testing,andrunningLet'sgettobusinessnow.Wewillcompile,runtests,andrunourwebapplicationusingActivatorandSBT.Firstofall,let'scompile.Type$activatorcompileontheconsoleasfollows:

$activatorcompile

diego@4winds:~/bin/activator-1.3.10-minimal/bin/ReactiveWebStore$

activatorcompile

[info]Loadingglobalpluginsfrom/home/diego/.sbt/0.13/plugins

[info]Loadingprojectdefinitionfrom/home/diego/bin/activator-

1.3.10-minimal/bin/ReactiveWebStore/project

[info]SetcurrentprojecttoReactiveWebStore(inbuild

file:/home/diego/bin/activator-1.3.10-

minimal/bin/ReactiveWebStore/)

[info]Updating{file:/home/diego/bin/activator-1.3.10-

minimal/bin/ReactiveWebStore/}root...

[info]Compiling14Scalasourcesand1Javasourceto

/home/diego/bin/activator-1.3.10-

minimal/bin/ReactiveWebStore/target/scala-2.11/classes...

[success]Totaltime:154s,completed14/05/201619:28:03

diego@4winds:~/bin/activator-1.3.10-minimal/bin/ReactiveWebStore$

Let'srunourtestsnowwiththecommand$activatortest.

Page 149: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Finally,it'stimetorunyourapplication.Type$activatorrunontheconsole.

Openyourwebbrowser,andgototheURL:http://localhost:9000.

Youshouldseeascreenlikethefollowing:

Page 150: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryCongratulations!You'vejustBootstrappedyourfirstScala/Playframeworkfirst.Activatormakesourlifeeasier.Asyoucansee,withthreecommands,wewereabletogetasiteupandrunning.YoucoulddothesamewithjustSBT,however,itwouldtakemoretime,becausewewouldneedtogetallthedependencies,configureallthesourcecodestructure,andaddsomesampleHTMLandScalacode.ThankstoActivator,wedon'tneedtodoanyofthat.However,wecanstillchangealltheSBTfilesandconfigsasperourwish.ActivatorisnottightwithScalaorourapplicationcode,sinceitismorelikeatemplate-basedcodegenerator.

Inthenextchapter,wewillbeimprovingtheapplicationbyaddingvalidations,databasepersistence,ReactiveMicroservicescallingusingRxScalaandScala,andmuchmore.

Page 151: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter3.DevelopingtheUIwithPlayFrameworkInthepreviouschapter,weperformedbootstrappingonourapplicationusingActivator.Inthischapter,wewillcontinuedevelopingourwebapplicationusingScalaandPlayframework.Playframeworkisgreatforwebdevelopmentbecauseitissimpletouse,andatthesametime,verypowerful.Thisisbecauseitusestop-notchreactivesolutionslikespray,Akka,andAkkaStreamunderthehood.Forthischapter,wewillcreatethebasicUIforsomepartsofourreactivewebsolutionbyaddingvalidationandanin-memorystoresoyoucanfeeltheapplicationworking.WewillusealittlebitofCSSforstyling,andJavaScriptforsomesimplevisualizations.

Inthischapter,wewillcoverthefollowingtopics:

BasicsofwebdevelopmentwithScalaandPlayframeworksCreatingyourmodelsWorkingwithviewsandvalidationsWorkingwithsessionscopes

Page 152: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GettingstartedLet'shavealookatthepreviewofReactiveWebStore--theapplicationthatwewillbuild.

Fornow,wewillbuildthreesimpleoperations--Create,Retrieve,Update,andDelete(CRUD)inordertomanageproducts,productreviews,andproductimages.Wewillcreatemodels,controllers,views,androutesforeachCRUD.

Let'sgetstarted.Firstofall,weneedtodefineourmodels.ThemodelsneedtobelocatedatReactiveWebStore/app/models.ModelsaretheCOREofthesystemandtheyrepresenttheentity.WewillusethisentitylatertostoreandretrievedatafromadatabaselateroninChapter6,PersistencewithSlick.OurmodelsshouldnothaveanyUIlogic,sinceweshouldusecontrollersforUIlogic.

Page 153: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingourmodelsForourproductmodel,wehaveasimpleScalacaseclassinProduct.scalaasfollows:

packagemodels

caseclassProduct

(varid:Option[Long],

varname:String,

vardetails:String,

varprice:BigDecimal)

{

overridedeftoString:String=

{

"Product{id:"+id.getOrElse(0)+",name:"+name+",

details:"+details+",price:"+price+"}"

}

}

AproductcanhaveanoptionalID,aname,details,andaprice.WealsooverridethetoStringmethodjustforthesakeofsimplicityforlogging.Wealsoneedtodefinemodelsforimageandreview.

ThefollowingisthereviewmodelfromReview.scala:

packagemodels

caseclassReview

(varid:Option[Long],

varproductId:Option[Long],

varauthor:String,

varcomment:String)

{

overridedeftoString:String={

"Review{id:"+id+",productId:"+

productId.getOrElse(0)+",author:"+author+",comment:

"+comment+"}"

}

}

Forareviewmodel,wehaveanoptionalID,anoptionalproductId,oneauthor,andacomment.Validationswillbedoneontheviews.Nowlet'sgofortheimagemodel.

TheimagemodelcanbefoundinImage.scalaasfollows:

packagemodels

caseclassImage

(varid:Option[Long],

varproductId:Option[Long],

varurl:String){

overridedeftoString:String={

"Image{productId:"+productId.getOrElse(0)+",url:"

+url+"}"

Page 154: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

}

Foranimagemodel,wehaveanoptionalID,anoptionalproductId,andtheimageURL.

ThePlayframeworkdoestherouting,andweneedtodefinetheroutesatReactiveWebStore/conf/routes.KeepinmindthatthePlayframeworkwillvalidatealltheroutes,soyouneedtospecifyvalidpackagesandclasses.Playalsocreatessomethingcalledreversecontroller,whichwewilluselaterinthechapter.Fornow,let'sdefinetheroutes.ReversecontrollerisgeneratedbythePlayframeworkwithanactionmethodwhichisthesameasthatoftheoriginalcontrollerwiththesamesignature,butitreturnsplay.api.mvc.Callinsteadofplay.api.mvc.Action.

Page 155: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingroutesThePlayframeworkCRUDoperations'routesforproduct,image,andreviewareasfollows:

#Routes

#Thisfiledefinesallapplicationroutes(Higherpriorityroutesfirst)

#~~~~

GET/controllers.HomeController.index

GET/assets/*filecontrollers.Assets.at(path="/public",file)

#

#CompleteCRUDforProduct

#

GET/productcontrollers.ProductController.index

GET/product/addcontrollers.ProductController.blank

POST/product/controllers.ProductController.insert

POST/product/:idcontrollers.ProductController.update(id:Long)

POST/product:id/removecontrollers.ProductController.remove(id:Long)

GET/product/details/:idcontrollers.ProductController.details(id:Long)

#

#CompleteGRUDforReview

#

GET/reviewcontrollers.ReviewController.index

GET/review/addcontrollers.ReviewController.blank

POST/review/controllers.ReviewController.insert

POST/review/:idcontrollers.ReviewController.update(id:Long)

POST/review:id/removecontrollers.ReviewController.remove(id:Long)

GET/review/details/:idcontrollers.ReviewController.details(id:Long)

#

#CompleteCRUDforImage

#

GET/imagecontrollers.ImageController.index

GET/image/addcontrollers.ImageController.blank

POST/image/controllers.ImageController.insert

POST/image/:idcontrollers.ImageController.update(id:Long)

POST/image:id/removecontrollers.ImageController.remove(id:Long)

GET/image/details/:idcontrollers.ImageController.details(id:Long)

Theroutesworklikethis--FirstyouneedtodefinetherestverbsuchasGET,POST,PUT,DELETE,andthenyouputinaPATHlike/image.Finally,youspecifywhichcontrollerfunctionwillhandlethatroute.Nowwehavetheroutesinplace,wecanmovetothecontrollers.Wewilldefinethecontrollersforproduct,imageandreview.

Alltheroutesfollowthesamelogic.Firstwesendtheusertothewebpagewherewelistalltheitems(products,images,reviews)--thisisrepresentedbyGET/resource,wheretheresourcecanbeanimage,product,orreview,forinstance.Inordertogetaspecificresource,oftenbyID,wegivethecommandGET/resource(product,revieworimage)/ID.POST/resourceisusedtoperformtheUPDATE.

Page 156: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Inordertocreateanewitem(product,review,orimage),thepatternisGET/resource/addandPOST/resource/.Youmaywonderwhytherearetworoutestoperformaninsert.Well,that'sbecausefirstofallweneedtoloadthewebpage,andsecondly,whentheformissubmitted,weneedanewroutetohandlethevalues.Therearetworoutesforanupdateaswellforthesamereason.IfyouwanttoDELETEaresource,thepatternisPOST/resource/ID/remove.Lastly,wehavethedetailsoftheoperation,whichisusedtoshowdetailedinformationwithregardtoaspecificitem--thepatternisGET/resource/details/ID.Withsixroutes,wecandoacompleteCRUDforaresourcesuchasproduct,image,andreview,oranyotherfutureresourcethatyoumayaddtothisapplicationoryourownapplications.

Page 157: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingourcontrollersNowlet'smovetothecontrollersusedonthepreviousroutes.ThecontrollersneedtobelocatedatReactiveWebStore/app/controllers.Controllersareboundbetweenviews(UI),modelsandservice,whichareresponsibleforbusinessoperations.It'salwaysimportanttoseparateUIlogic,whichtendstobespecific,frombusinesslogic,whichtendstobemoregeneric,andoften,waymoreimportant.

Let'shavealookattheproductcontrollerinProductController.scalainthefollowingcode:

@Singleton

classProductController@Inject()(valmessagesApi:MessagesApi,val

service:IProductService)extendsControllerwithI18nSupport{

valproductForm:Form[Product]=Form(

mapping(

"id"->optional(longNumber),

"name"->nonEmptyText,

"details"->text,

"price"->bigDecimal

)(models.Product.apply)(models.Product.unapply))

defindex=Action{implicitrequest=>

valproducts=service.findAll().getOrElse(Seq())

Logger.info("indexcalled.Products:"+products)

Ok(views.html.product_index(products))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.product_details(None,productForm))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valproduct=service.findById(id).get

Ok(views.html.product_details(Some(id),

productForm.fill(product)))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

productForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.product_details(None,form))

},

product=>{

valid=service.insert(product)

Redirect(routes.ProductController.index).flashing("success"

->Messages("success.insert",id))

})

}

Page 158: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

productForm.bindFromRequest.fold(

form=>{

Ok(views.html.product_details(Some(id),

form)).flashing("error"->"Fixtheerrors!")

},

product=>{

service.update(id,product)

Redirect(routes.ProductController.index).

flashing("success"->Messages("success.update",

product.name))

})

}

defremove(id:Long)=Action{

service.findById(id).map{product=>

service.remove(id)

Redirect(routes.ProductController.index).flashing("success"->

Messages("success.delete",product.name))

}.getOrElse(NotFound)

}

}

ThePlayframeworkusesdependencyinjectionandinversionofcontrolusingGoogleGuice.So,youcanseeatthetopofthecontrollerthatwehavetheannotations@Singletonand@Inject.SingletonmeansthatGuicewillcreateasingleinstanceoftheclasstohandleallrequests.Injectmeansweareinjectingotherdependenciesintoourcontroller,forinstance,weinjectMessagesApiinordertohavethePlayframeworkinternalizationsupportforstringmessages,andIProductService,thatis,theproductservicethatwewillcoverlaterinthischapter.

WealsoneedtoextendthePlayclass,play.api.mvc.Controller.Eachfunctioninacontrollerneedstoreturnanaction.Thisactioncouldbeaview.ThePlayframeworkcompilesalltheviewsintoScalaclasses,soyoucansafelyreferencethemintoyourcontrollerscode.

AllbusinessoperationsaredelegatedtoatraitcalledIProductService,whichwewillcoverlaterinthischapter.WealsologsomeinformationusingtheLoggerclass.PlayFrameworkusesLogbackasthedefaultloggingsolution.Let'stakeacloserlookateachcontrollerfunctionnow.

TheindexfunctioncallsIProductService,andfindsalltheavailableproducts.Iftherearenoproductsavailable,itreturnsanemptysequence,andthencallstheproductUIpassingthecollectionofproducts.

Theblankfunctionrendersablankproductform,sotheusercanhaveablankproductformontheUIinordertoadddata(insertoperation).Playframeworkworkswithformbinding.

Page 159: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

So,ineachcontroller,youneedtodefinehowyourformlooksontheUI.Thatformmappingisdoneusingplay.api.data.Form.YoucanseethemappingontheimmutablevariablecalledproductForm.Themappingisbetweentheview(UI)andthemodelcalledproduct.KeepinmindthatthenamefieldismappedasNonEmptyText,whichmeansPlaywon'tacceptnullorblankvalues.Thisisagreatfuture,becausewecandovalidationsinadeclarativewaywithouthavingtowritecode.PriceisdefinedasBigDecimal,soPlaywon'taccepttext,butonlynumbers.

ThedetailsfunctionretrievesaproductusingIProductService,andredirectstotheview.However,beforedoingtheredirect,itbindsthedatawiththeformsotheUIwillloadwithallthedataintotheHTMLinputs.

Wealsohavetheinsertandupdatemethods.Theyareallconstructedwithafoldmethod.Thefoldmethodhasleftandright,whichmeanserrororok.Thefoldfunctioniscalledfromthemappedformandiftherearenovalidationerrors,itgoesright,butiftherearevalidationserrors,itgoesleft.That'saverysimpleandcleanwaytocodetheupdateandinsertflows.Withfold,wedon'tneedcodeforanifstatement.OncethevalidationisOK,wecallIProductServicetodoaninsertorupdate,andthenweperformaredirecttotheview.Messagesarepassedviascope.Playhasoptionsforscope--sessionorFlash.Sessionisformultiplerequests,andthevaluewillbestoredintheclientside.Flashisarequestscope,andmostofthetimesthatiswhatyouneedtouse.HereweareusingtheFlashscope,soitwillonlyexitduringthatspecificrequest.ThisfeatureisusedtopassInternationalizationmessages(i18n),whicharetheresultoftheaction.Allthei18nmessagesneedtobedefinedatReactiveWebStore/conf/messagesasfollows:

success.delete=OK'{0}'deleted!

success.insert=OK'{0}'created!

success.update=OK'{0}'updated!

error.notFound=NothingFoundwithID{0,number,0}

error.number=Notavalidnumber

error.required=Missingvaluehere

Lastly,wehavetheremovemethod.Firstofall,weneedtomakesuretheproductexists,sowedoafindByIdusingIProductService,andthenweapplyamapfunction.Iftheproductdoesn'texist,thePlayframeworkhasprebuiltHTTPerrorcodemessageslikeNotFound.Iftheproductexists,weremoveitusingIProductService,andthenweredirecttotheUIwithaflashingmessage.Nowlet'sseetheimageandreviewcontrollers.

Thereviewcontroller,ReviewController.scala,isasfollows:

@Singleton

classReviewController@Inject()

(valmessagesApi:MessagesApi,

valproductService:IProductService,

valservice:IReviewService)

extendsControllerwithI18nSupport{

valreviewForm:Form[Review]=Form(

mapping(

Page 160: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

"id"->optional(longNumber),

"productId"->optional(longNumber),

"author"->nonEmptyText,

"comment"->nonEmptyText

)(models.Review.apply)(models.Review.unapply))

defindex=Action{implicitrequest=>

valreviews=service.findAll().getOrElse(Seq())

Logger.info("indexcalled.Reviews:"+reviews)

Ok(views.html.review_index(reviews))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.review_details(None,

reviewForm,productService.findAllProducts))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valreview=service.findById(id).get

Ok(views.html.review_details(Some(id),

reviewForm.fill(review),productService.findAllProducts))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

reviewForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.review_details(None,

form,productService.findAllProducts))

},

review=>{

if(review.productId==null||

review.productId.getOrElse(0)==0){

Redirect(routes.ReviewController.blank).flashing("error"->

"ProductIDCannotbeNull!")

}else{

Logger.info("Review:"+review)

if(review.productId==null||

review.productId.getOrElse(0)==0)thrownew

IllegalArgumentException("ProductIdCannotBeNull")

valid=service.insert(review)

Redirect(routes.ReviewController.index).flashing("success"-

>Messages("success.insert",id))

}

})

}

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

reviewForm.bindFromRequest.fold(

form=>{

Ok(views.html.review_details(Some(id),

form,productService.findAllProducts)).flashing("error"->

"Fixtheerrors!")

Page 161: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

},

review=>{

service.update(id,review)

Redirect(routes.ReviewController.index).flashing("success"-

>Messages("success.update",review.productId))

})

}

defremove(id:Long)=Action{

service.findById(id).map{review=>

service.remove(id)

Redirect(routes.ReviewController.index).flashing("success"-

>Messages("success.delete",review.productId))

}.getOrElse(NotFound)

}

}

Thereviewcontrollerfollowsthesameideasandstructureastheproductcontroller.TheonlymaindifferenceisthathereweneedtoInjectIProductService,becauseareviewneedstobelongtoaproduct.ThenweneedtouseIProductServiceinordertofindAllProduct,becauseinthereviewview,wewillhaveSelectBoxwithalltheavailableproducts.

Theimagecontroller,ImageController.scala,isasfollows:

@Singleton

classImageController@Inject()

(valmessagesApi:MessagesApi,

valproductService:IProductService,

valservice:IImageService)

extendsControllerwithI18nSupport{

valimageForm:Form[Image]=Form(

mapping(

"id"->optional(longNumber),

"productId"->optional(longNumber),

"url"->text

)(models.Image.apply)(models.Image.unapply))

defindex=Action{implicitrequest=>

valimages=service.findAll().getOrElse(Seq())

Logger.info("indexcalled.Images:"+images)

Ok(views.html.image_index(images))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.image_details(None,

imageForm,productService.findAllProducts))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valimage=service.findById(id).get

Ok(views.html.image_details(Some(id),

Page 162: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

imageForm.fill(image),productService.findAllProducts))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

imageForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.image_details(None,form,

productService.findAllProducts))

},

image=>{

If(image.productId==null||

image.productId.getOrElse(0)==0){

Redirect(routes.ImageController.blank).

flashing("error"->"ProductIDCannotbeNull!")

}else{

if(image.url==null||"".equals(image.url))image.url

="/assets/images/default_product.png"

valid=service.insert(image)

Redirect(routes.ImageController.index).

flashing("success"->Messages("success.insert",id))

}

})

}

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

imageForm.bindFromRequest.fold(

form=>{

Ok(views.html.image_details(Some(id),form,

null)).flashing("error"->"Fixtheerrors!")

},

image=>{

service.update(id,image)

Redirect(routes.ImageController.index).

flashing("success"->Messages("success.update",

image.id))

})

}

defremove(id:Long)=Action{

service.findById(id).map{image=>

service.remove(id)

Redirect(routes.ImageController.index).flashing("success"

->Messages("success.delete",image.id))

}.getOrElse(NotFound)

}

}

ImagereviewworksinasimilarwaytoReviewController.WeneedIProductServicetogetalltheservices.

Page 163: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WorkingwithservicesServicesarewhereweputthebusinesslogic.WewilllookatreactivepersistenceinChapter6,PersistencewithSlick.Rightnow,wedon'thaveadatabasetopersistinformation,so,fornow,wewilldoanin-memorypersistence.

Firstwewilldefinethecontractofourservices.ThisistheBaseAPIthatwewilluseinthecontrollers.Let'stakealookatthefollowingtraitinBaseService.scala:

packageservices

importjava.util.concurrent.atomic.AtomicLong

importscala.collection.mutable.HashMap

traitBaseService[A]{

varinMemoryDB=newHashMap[Long,A]

varidCounter=newAtomicLong(0)

definsert(a:A):Long

defupdate(id:Long,a:A):Boolean

defremove(id:Long):Boolean

deffindById(id:Long):Option[A]

deffindAll():Option[List[A]]

}

Intheprecedingcode,wehaveanin-memorymutableHashMap,whichisinourmemorydatabasewherewewillstoreproducts,images,andreviews.WealsohaveanatomiccounterwithwhichwecangenerateIDsforourmodels.ThisisatraitusingGenerics--asyoucansee,herewehavealltheoperationswithA,whichwillbespecifiedlater.Nowwecanmovetheserviceimplementationforproduct,review,andimage.

TheProductService.scalapackageisasfollows:

packageservices

importmodels.Product

importjavax.inject._

traitIProductServiceextendsBaseService[Product]{

definsert(product:Product):Long

defupdate(id:Long,product:Product):Boolean

defremove(id:Long):Boolean

deffindById(id:Long):Option[Product]

deffindAll():Option[List[Product]]

deffindAllProducts():Seq[(String,String)]

}

@Singleton

classProductServiceextendsIProductService{

definsert(product:Product):Long={

Page 164: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

valid=idCounter.incrementAndGet()

product.id=Some(id)

inMemoryDB.put(id,product)

id

}

defupdate(id:Long,product:Product):Boolean={

validateId(id)

product.id=Some(id)

inMemoryDB.put(id,product)

true

}

defremove(id:Long):Boolean={

validateId(id)

inMemoryDB.remove(id)

true

}

deffindById(id:Long):Option[Product]={

inMemoryDB.get(id)

}

deffindAll():Option[List[Product]]={

if(inMemoryDB.values==Nil||

inMemoryDB.values.toList.size==0)returnNone

Some(inMemoryDB.values.toList)

}

privatedefvalidateId(id:Long):Unit={

valentry=inMemoryDB.get(id)

if(entry==null)thrownewRuntimeException("Couldnotfind

Product:"+id)

}

deffindAllProducts():Seq[(String,String)]={

valproducts:Seq[(String,String)]=this

.findAll()

.getOrElse(List(Product(Some(0),"","",0)))

.toSeq

.map{product=>(product.id.get.toString,product.name)}

returnproducts

}

}

Inthelastcode,wedefinedatraitcalledIProductService,whichextendsBaseServicewithagenericapplytoproduct.TheProductServicepackageimplementsIProductService.InScala,wecanhavemultipleclassesinthesameScalafile,sothereisnoneedtocreatedifferentfiles.

Thecodeisverystraightforward.ThereisautilitymethodherecalledfindAllProducts,whichisusedbyreviewandimagecontrollers.Herewegetalltheelementsonthein-

Page 165: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

memoryhashmap.Iftherearenoelements,wereturnalistwithemptyproduct.ThenwemapthelisttoaSeqoftuple,whichisrequiredbytheSelectBoxcheckboxthatwewillhaveintheview(UI).Nowlet'sgofortheimageandreviewservicesasfollows:

packageservices

importjavax.inject._

importmodels.Image

importscala.collection.mutable.HashMap

importjava.util.concurrent.atomic.AtomicLong

traitIImageServiceextendsBaseService[Image]{

definsert(image:Image):Long

defupdate(id:Long,image:Image):Boolean

defremove(id:Long):Boolean

deffindById(id:Long):Option[Image]

deffindAll():Option[List[Image]]

}

@Singleton

classImageServiceextendsIImageService{

definsert(image:Image):Long={

valid=idCounter.incrementAndGet();

image.id=Some(id)

inMemoryDB.put(id,image)

id

}

defupdate(id:Long,image:Image):Boolean={

validateId(id)

image.id=Some(id)

inMemoryDB.put(id,image)

true

}

defremove(id:Long):Boolean={

validateId(id)

inMemoryDB.remove(id)

true

}

deffindById(id:Long):Option[Image]={

inMemoryDB.get(id)

}

deffindAll():Option[List[Image]]={

if(inMemoryDB.values.toList==null||

inMemoryDB.values.toList.size==0)returnNone

Some(inMemoryDB.values.toList)

}

privatedefvalidateId(id:Long):Unit={

valentry=inMemoryDB.get(id)

If(entry==null)thrownewRuntimeException("Couldnotfind

Image:"+id)

Page 166: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

}

Intheprecedingcode,wehavesomethingprettysimilartothatofProductService.WehaveatraitcalledIImageServiceandtheImageServiceimplementation.Nowlet'sgoforthereviewserviceimplementationinReviewService.scalaasfollows:

packageservices

importjavax.inject._

importmodels.Review

importscala.collection.mutable.HashMap

importjava.util.concurrent.atomic.AtomicLong

traitIReviewServiceextendsBaseService[Review]{

definsert(review:Review):Long

defupdate(id:Long,review:Review):Boolean

defremove(id:Long):Boolean

deffindById(id:Long):Option[Review]

deffindAll():Option[List[Review]]

}

@Singleton

classReviewServiceextendsIReviewService{

definsert(review:Review):Long={

valid=idCounter.incrementAndGet();

review.id=Some(id)

inMemoryDB.put(id,review)

id

}

defupdate(id:Long,review:Review):Boolean={

validateId(id)

review.id=Some(id)

inMemoryDB.put(id,review)

true

}

defremove(id:Long):Boolean={

validateId(id)

inMemoryDB.remove(id)

true

}

deffindById(id:Long):Option[Review]={

inMemoryDB.get(id)

}

deffindAll():Option[List[Review]]={

if(inMemoryDB.values.toList==null||

inMemoryDB.values.toList.size==0)returnNone

Some(inMemoryDB.values.toList)

}

Page 167: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

privatedefvalidateId(id:Long):Unit={

valentry=inMemoryDB.get(id)

If(entry==null)thrownewRuntimeException("Couldnotfind

Review:"+id)

}

}

Intheprecedingcode,wehavetheIReviewServicetraitandtheReviewServiceimplementation.Wehavevalidationsontheserviceaswellasagoodpractice.

Page 168: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringtheGuicemoduleWeinjectedclassesusing@Injectinourcontrollers.Theinjectionhappensbasedonatrait;weneedtodefineaconcreteimplementationforthetraitsweinjected.ThePlayframeworklooksforGuiceinjectionsatthelocationReactiveWebStore/app/Module.scala.Okay,solet'sdefineourinjectionsforthethreecontrollerswejustcreated.

TheGuicemoduleisfoundinModule.scalaasfollows:

importcom.google.inject.AbstractModule

importjava.time.Clock

importservices.{ApplicationTimer}

importservices.IProductService

importservices.ProductService

importservices.ReviewService

importservices.IReviewService

importservices.ImageService

importservices.IImageService

/**

*ThisclassisaGuicemodulethattellsGuicehowtobindseveral

*differenttypes.ThisGuicemoduleiscreatedwhenthePlay

*applicationstarts.

*Playwillautomaticallyuseanyclasscalled`Module`thatisin

*therootpackage.Youcancreatemodulesinotherlocationsby

*adding`play.modules.enabled`settingstothe`application.conf`

*configurationfile.

*/

classModuleextendsAbstractModule{

overridedefconfigure()={

//UsethesystemclockasthedefaultimplementationofClock

bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)

//AskGuicetocreateaninstanceofApplicationTimer

//whentheapplicationstarts.

bind(classOf[ApplicationTimer]).asEagerSingleton()

bind(classOf[IProductService]).to(classOf[ProductService]).

asEagerSingleton()

bind(classOf[IReviewService]).to(classOf[ReviewService]).

asEagerSingleton()

bind(classOf[IImageService]).to(classOf[ImageService]).

asEagerSingleton()

}

}

Sowejustneedtoaddbindwithourtraitsforthecontrollers,andthenpointtothecontrollerimplementation.Theyshouldalsobecreatedassingletons,asthePlayframeworkstartsourapplication.Hereyoualsocandefineanyotherconfigurationorinjectionthatourapplicationmayneed.Thelastcodedefinesthreeservices:productservice,IReviewService,andIImageService.

Page 169: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Workingwithviews(UI)ThePlayframeworkworkswithaScala-basedtemplatingenginecalledTwirl.TwirlwasinspiredbyASP.NETRazor.Twirliscompactandexpressive;youwillseewecandomorewithless.Twirltemplatefilesaresimpletextfiles,however,thePlayframeworkcompilesthetemplatesandturnsthemintoScalaclasses.YoucanmixHTMLwithScalasmoothlyinTwirl.

TheUIwillbecompiledintoaScalaclass,thatcanandwillbereferencedatourcontrollers,becausewecanroutetoaview.Thenicethingaboutitisthatthismakesourcodingwaysafer,sincewehavethecompilercheckingforus.ThebadnewsisthatyouneedtocompileyourUI,otherwise,yourcontrollerswon'tfindit.

Previouslyinthischapter,wedefinedcontrollersforproducts,images,andreviews,andwewrotethefollowingcode:

Ok(views.html.product_details(None,productForm))

Withtheprecedingcode,weredirecttheusertoablankpageforproductssothattheusercancreateanewproduct.WealsocanpassparameterstotheUI.SinceitisallScalacode,youareactuallyjustcallingafunctionasfollows:

valproduct=service.findById(id).get

Ok(views.html.product_details(Some(id),productForm.fill(product)))

Intheprecedingcode,wecalltheservicetoretrieveaproductbyID,andthenpasstheobjecttotheUIwiththeformbeingfilled.

Let'scontinuebuildingourapplicationandcreatetheUIfortheproducts,reviews,andimages.SincewearedoingaCRUD,wewillneedmorethanonetemplatefileperCRUD.Wewillneedthefollowingstructure:

IndexTemplateListallitemsLinktoeditoneitemLinktoremoveoneitemLinktocreateanewItem

DetailTemplateHTMLFormtocreateanewItemHTMLformtoeditanexistingitem(forupdate)

Havingsaidthat,wewillhavethefollowingfiles:

ForProducts:product_index.scala.htmlproduct_details.scala.html

ForImage:

Page 170: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

image_index.scala.htmlimage_details.scala.html

ForReviews:review_index.scala.htmlreview_details.scala.html

Forthesakeofcodereuse,wewillcreateanotherfilecontainingthebasicstructureofourUI,likeCSSimports(CSSneedstobelocatedatReactiveWebStore\public\stylesheets),JavaScriptimports,andpagetitlesothatwedon'tneedtorepeatthatinallthetemplatesforeachCRUD.Thispagewillbecalled:main.scala.html.

AlltheUIcodeshouldbelocatedatReactiveWebStore/app/views.

ThemainScalawiththeUIindexforallCRUDoperationsisinmain.scala.html,asfollows:

@(title:String)(content:Html)(implicitflash:Flash)

<!DOCTYPEhtml>

<htmllang="en">

<head>

<title>@title</title>

<linkrel="shortcuticon"type="image/png"

href="@routes.Assets.at("images/favicon.png")">

<linkrel="stylesheet"media="screen"

href="@routes.Assets.at("stylesheets/main.css")">

<linkrel="stylesheet"media="screen"

href="@routes.Assets.at("stylesheets/bootstrap.min.css")">

<scriptsrc="@routes.Assets.at("javascripts/jquery-

1.9.0.min.js")"type="text/javascript"></script>

<scriptsrc="@routes.Assets.at("javascripts/bootstrap.js")"

type="text/javascript"></script>

<scriptsrc="@routes.Assets.at("javascripts/image.js")"

type="text/javascript"></script>

</head>

<body>

<center><ahref='/'><imgheight='42'width='42'

src='@routes.Assets.at("images/rws.png")'></a>

<h3>@title</h3></center>

<divclass="container">

@alert(alertType:String)={

@flash.get(alertType).map{message=>

<divclass="alertalert-@alertType">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

@message

</div>

}

}

@alert("error")

@alert("success")

@content

<ahref="/"></a><BR>

<buttontype="submit"class="btnbtn-primary"

Page 171: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

onclick="window.location.href='/';">

ReactiveWebStore-Home

</button>

</div>

</body>

</html>

Intheprecedingcode,firstofall,thereisthefollowinglineattheverytop:

@(title:String)(content:Html)(implicitflash:Flash)

ThismeansthatwedefinetheparametersthisUIcanreceive.Hereweexpectastringtitle,whichwillbethepagetitle,andtherearesomecurryingvariablesaswell.YoucangetmoredetailsaboutcurryinginChapter1,IntroductiontoFP,Reactive,andScala.Soincurrying,therearetwothings:FirstisHTML,whichmeansyoucanpassHTMLcodetothisfunction,andsecond,wehaveFlashwhichthePlayframeworkwillpassforus.Flashisusedtogetparametersbetweenrequests.

Asyoucansee,laterinthecodewehave@title,whichmeansweretrievethetitleoftheparameters,andaddthevaluetotheHTML.Wealsoprintanyerrormessageoranyvalidationsissues,ifpresent,[email protected],butwedonotputhard-codedpaths.Instead,weusetherouterslike@routes.Assets.at.TheJavascriptsstillneedtobelocatedatReactiveWebStore\public\javascripts.

Nowothertemplatescanworkwith@main(..),andtheydon'tneedanydeclarationofJavascriptorCSS.TheycanaddanextraHTMLcode,[email protected],fortheproducts,thecontentwillbeHTMLproductcontent,andsoonforreviews,andimages.NowwecanmovefortheproductsUI.

Productindex:UIindexforproductsproduct_index.scala.html

@(products:Seq[Product])(implicitflash:Flash)

@main("Products"){

@if(!products.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>Name</th>

<th>Details</th>

<th>Price</th>

<th></th>

</tr>

@for(product<-products){

<tr>

<td><ahref="@routes.ProductController.

details(product.id.get)">@product.name</a></td>

<td>@product.details</td>

<td>@product.price</td>

<td><formmethod="post"action=

"@routes.ProductController.remove(product.id.get)">

Page 172: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

<buttonclass="btnbtn-link"type="submit">

<iclass="icon-trash"></i>Delete</button>

</form></td>

</tr>

}

</table>

}

<p><ahref="@routes.ProductController.blank"class=

"btnbtn-success"><iclass="icon-plusicon-white">

</i>AddProduct</a></p>

}

Asyoucansee,thereisHTMLmixedwithScalacode.EverytimeyouneedtorunHTML,youjustrunit,andwhenyouneedrunScalacode,youneeduseaspecialcharacter,@.Attheverytopofthetemplate,youcanseethefollowingcode:

@(products:Seq[Product])(implicitflash:Flash)

SincethisisScalacodeintheend,andwillbecompiled,weneedtodefinewhatparametersthisUItemplatecanreceive.Hereweexpectasequenceofproducts.ThereisalsoacurryingimplicitvariablecalledFlash,whichwillbeprovidedbythePlayframework,andwewilluseitforthemessagedisplay.Wealsohavethecode-@main("Products"){..}.ThismeansthatwecallthemainScalatemplateandaddextraHTML--theproductHTML.ForthisproductUI,welistalltheproductsbasedonthesequenceofproducts.Asyoucansee,wedefineanHTMLtable.Wealsovalidateifthesequenceisnotemptybeforelistingalltheproducts.

Nowwecangoforthedetailspageforproductsinproduct_details.scala.htmlasfollows:

@(id:Option[Long],product:Form[Product])(implicitflash:Flash)

@importplay.api.i18n.Messages.Implicits._

@importplay.api.Play.current

@main("Product:"+product("name").value.getOrElse("")){

@if(product.hasErrors){

<divclass="alertalert-error">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

Sorry!Someinformationdoesnotlookright.Couldyou

reviewitpleaseandre-submit?

</div>

}

@helper.form(action=if(id.isDefined)

routes.ProductController.update(id.get)else

routes.ProductController.insert)

{

@helper.inputText(product("name"),'_label->"Product

Name")

@helper.inputText(product("details"),'_label->"Product

Details")

Page 173: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

@helper.inputText(product("price"),'_label->"Price")

<divclass="form-actions">

<buttontype="submit"class="btnbtn-primary">

@if(id.isDefined){UpdateProduct}else{NewProduct}

</button>

</div>

}

}

ForthisprecedingUI,attheverytop,wehavethefollowingline:

@(id:Option[Long],product:Form[Product])(implicitflash:Flash)

ThismeansthatweexpectanIDwhichiscompletelyoptional.ThisIDisusedtoknowifwearedealingwithaninsertscenariooranupdatescenario,becauseweusethesameUIforbothinsertandupdate.Wealsogettheproductform,whichwillbepassedthroughProductController,andwereceiveFlash,whichwillbeprovidedbythePlayframework.

WeneedtodosomeimportsontheUIsothatwecangetaccesstothePlayframeworki18nsupport.Thisisdonebythefollowing:

@importplay.api.i18n.Messages.Implicits._

@importplay.api.Play.current

LikethepreviousUI,werenderthisUIwithinthemainScalaUI.Sowedon'tneedtospecifyJavaScriptandCSSagain.Thisisdonebythefollowingcode:

@main("Product:"+product("name").value.getOrElse("")){..}

Nextwecheckifthereareanyvalidationerrors.Ifthereare,theuserswillneedtofixtheerrorsbeforemovingon.Thisisdonebythefollowingcode:

@if(product.hasErrors){

<divclass="alertalert-error">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

Sorry!Someinformationdoesnotlookright.Couldyoureview

itpleaseandre-submit?

</div>

}

Nowisthetimetocreatetheproductform,which,intheend,willbemappedtoHTMLinputboxes.Wedothiswiththefollowingcode:

@helper.form(action=if(id.isDefined)

routes.ProductController.update(id.get)else

routes.ProductController.insert){

@helper.inputText(product("name"),'_label->"ProductName")

@helper.inputText(product("details"),'_label->"Product

Details")

@helper.inputText(product("price"),'_label->"Price")

<divclass="form-actions">

Page 174: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

<buttontype="submit"class="btnbtn-primary">

@if(id.isDefined){UpdateProduct}else{NewProduct}

</button>

</div>

}

@helper.formisaspecialhelperprovidedbythePlayframeworktocreateHTMLformseasily.So,theactionisthetargetwheretheformwillbesubmitted.Weneedtodoanifhere,sinceweneedtoknowifitisanupdateoraninsert.Thenwemapallthefieldswehaveforourproductmodelwiththiscode:

@helper.inputText(product("name"),'_label->"ProductName")

@helper.inputText(product("details"),'_label->"ProductDetails")

@helper.inputText(product("price"),'_label->"Price")

Remember,theproductformcomesfromtheproductcontroller.Forthehelper,wejustneedtotellitwhichproductfieldisforwhichHTMLlabel,andthat'sit.ThiswillproducethefollowingUIs.

ThefollowingimageshowstheblankproductindexUI:

TheinsertUIformforproductdetailslooksasfollows:

Page 175: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Withproductsadded,theproductindexUIappearsasfollows:

Page 176: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Nowwecanmovetoreviews.Let'sgofortheUIs.

ThereviewindexUIinreview_index.scala.htmlisasfollows:

@(reviews:Seq[Review])(implicitflash:Flash)

@main("Reviews"){

@if(!reviews.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>ProductId</th>

<th>Author</th>

<th>Comment</th>

<th></th>

</tr>

@for(review<-reviews){

<tr>

<td><ahref="@routes.ReviewController.details

(review.id.get)">@review.productId</a></td>

<td>@review.author</td>

<td>@review.comment</td>

<td>

<formmethod="post"action="@routes.ReviewController.

remove(review.id.get)">

<buttonclass="btnbtn-link"type="submit"><iclass=

"icon-trash"></i>Delete</button>

</form></td>

</tr>

}

</table>

}

<p><ahref="@routes.ReviewController.blank"class="btnbtn-

Page 177: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

success"><iclass="icon-plusicon-white"></i>AddReview</a></p>

}

Soherewehavethesamethingsaswehadfortheproducts.Let'stakealookatthedetailspageforreviewnow.Youcanfinditinreview_details.scala.html.

@(id:Option[Long],review:Form[Review],products:

Seq[(String,String)])(implicitflash:Flash)

@importplay.api.i18n.Messages.Implicits._

@importplay.api.Play.current

@main("review:"+review("name").value.getOrElse("")){

@if(review.hasErrors){

<divclass="alertalert-error">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

Sorry!Someinformationdoesnotlookright.Couldyou

reviewitpleaseandre-submit?

</div>

}

@helper.form(action=if(id.isDefined)

routes.ReviewController.update(id.get)else

routes.ReviewController.insert){

@helper.select(

field=review("productId"),

options=products,

'_label->"ProductName",

'_default->review("productId").value.getOrElse("Choose

One"))

@helper.inputText(review("author"),'_label->"Author")

@helper.inputText(review("comment"),'_label->

"Comment")

<divclass="form-actions">

<buttontype="submit"class="btnbtn-primary">

@if(id.isDefined){Updatereview}else{Newreview}

</button>

</div>

}

}

Here,inthislastcode,wehavealmosteverythingsimilartowhatwehadforproducts,however,thereisonebigdifference.ReviewneedstobeassociatedwithaproductID.That'swhy,weneedtohaveaselectfortheproducts,whichisfulfilledbyproducts:Seq[(String,String)].ThiscomesfromtheReviewControllercode.ThiscodeproducesthefollowingUIs.

TheblankreviewindexUIisshownasfollows:

Page 178: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TheinsertreviewdetailsUIlooksasfollows:

Page 179: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThereviewindexUIwithreviewswilllooklikethefollowingimage:

Page 180: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Nowwecanmovetothelastone:theimageUI.TheimageUIisverysimilartothereviewUI,becauseitdependsontheproductIDtoo.Let'sgoforit.

TheimageindexUIhasthefollowingcodeinimage_index.scala.html:

@(images:Seq[Image])(implicitflash:Flash)

@main("Images"){

@if(!images.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>ProductID</th>

<th>URL</th>

<th></th>

</tr>

@for(image<-images){

<tr>

<td><ahref="@routes.ImageController.details

(image.id.get)">@image.id</a></td>

<td>@image.productId</td>

<td>@image.url</td>

<td><formmethod="post"action=

"@routes.ImageController.remove(image.id.get)">

<buttonclass="btnbtn-link"type="submit">

<iclass="icon-trash"></i>Delete</button>

</form></td>

</tr>

}

</table>

}

<p><ahref="@routes.ImageController.blank"class=

Page 181: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

"btnbtn-success"><iclass="icon-plusicon-white">

</i>AddImage</a></p>

}

ImageDetailsUI[image_details.scala.html]

@(id:Option[Long],image:Form[Image],products:Seq[(String,String)])

(implicitflash:Flash)

@importplay.api.i18n.Messages.Implicits._

@importplay.api.Play.current

@main("Image:"+image("productId").value.getOrElse("")){

@if(image.hasErrors){

<divclass="alertalert-error">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

Sorry!Someinformationdoesnotlookright.Couldyouimage

itpleaseandre-submit?

</div>

}

@helper.form(action=if(id.isDefined)

routes.ImageController.update(id.get)else

routes.ImageController.insert){

@helper.select(field=image("productId"),

options=products,

'_label->"ProductName",

'_default->image("productId").value.getOrElse("Choose

One")

)

@helper.inputText(

image("url"),

'_label->"URL",

'_placeholder->"/assets/images/default_product.png",

'onchange->"javascript:loadImage();"

)

Visualization<br>

<imgid="imgProduct"height="42"width="42"

src="@image("url").value"></img>

<divclass="form-actions">

<buttontype="submit"class="btnbtn-primary">

@if(id.isDefined){UpdateImage}else{NewImage}

</button>

</div>

}

}

ThisUItemplatewillcreatethefollowingHTMLPages:

TheblankimageindexUIisshowninthefollowingimage:

Page 182: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TheinsertUIforimagedetailslooksasfollows:

Page 183: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThefollowingistheimageindexUIwithitems:

Page 184: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

NowwehaveacompleteworkingUIapplication.Therearecontrollers,models,views,andsimpleservicesaswell.Wealsohaveallvalidationsinplace.

Page 185: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtocreatecontrollers,models,services,views(usingTwirltemplating),Guiceinjections,androuting.WecoveredtheprinciplesofScalaWebDevelopmentusingthePlayframework.Bytheendofthechapter,wegottheapplicationwiththePlayframeworkupandrunning.

Inthenextchapter,wewilllearnmoreaboutservices.Asyoumayrealize,wedidsomesimpleservicesinthischapterforproducts,reviews,andimages,butnowwewillcontinueworkingwithservices.

Page 186: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter4.DevelopingReactiveBackingServicesInthepreviouschapter,youlearnedhowtoBootstrapyourapplicationusingActivator,andwedevelopedourwebapplicationusingScalaandthePlayframework.NowwewillenterintothereactiveworldofRxJavaandRxScala.

Inthischapter,wewillcoverthefollowingtopics:

ReactiveprogrammingprinciplesandtheReactiveManifestoUnderstandingtheimportanceofnon-blockingIOObservables,functions,anderrorhandlingwithRxRefactoringourcontrollersandmodelstocallourservicesAddingRxScalatoourservicesAddinglogging

Page 187: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GettingstartedwithreactiveprogrammingBuildingapplicationstodayisharderthanitwasbefore.Everythingnowismorecomplex:wehavetousemorecoresinprocessors,andwehavecloud-nativeapplicationswithhundredsofmachinesforasingleservice.Concurrentprogramminghasalwaysbeenhard,anditwillalwaysbeso,becauseitisdifficulttomodeltime.Inordertoaddressthis,weneedtohaveareactivestyleofarchitecture.Inordertobeabletohandlemoreusersandscaleourapplications,weneedtoleverageAsyncandnon-blockingIO.Tohelpuswiththistask,wecanrelyonRxJavaandRxScala.Beingreactiveisnotonlyaboutcodebutalsoaboutarchitecturalprinciples.

TheReactiveManifestocapturestheseprinciplesverywell,andthereareacoupleoftechnologiesthatfollowtheseprinciplesinordertobefullyreactive.

TheReactiveManifestocanbeshownasinthefollowingdiagram:

Formoreinformation,youcanvisithttp://www.reactivemanifesto.org/.

TheReactiveManifestodescribeswhatthisreactivearchitecture/systemlookslike.Basically,therearethefollowingfourcoreprinciplesunderliningthereactiveidea:

Responsive:Thesystemshouldrespondinatimelymanner.Inotherwords,thesystemshoulddetectproblemsquickly,anddealwiththemeffectively,apartfromprovidingrapidandconsistentresponsetime.Resilient:Thesystemshouldstayresponsiveevenafterfailure.Thisisdoneviareplicationcontainment,isolation,anddelegation(https://en.wikipedia.org/wiki/Delegation_pattern).Containmentandisolationareideasthatcomefromthenavalindustry,andaredefinedbythebulkheadpattern(https://en.wikipedia.org/wiki/Bulkhead_(partition)).Failuresarecontainedateachcomponent.Doingsomakessurethatonesystem'sfailuredoesnotaffectothersystems.Recoveryisdelegatedtoanothersystem,andnottotheclient.

Page 188: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Elastic:Theabilitytoincreaseanddecreaseresourcesforthesystem.ThisrequiresyoudesignyoursystemwithoutSinglePointOfFailure(SPOF),anddesignusingshardsandreplication.Reactivesystemsarepredictiveandcost-effective.Message-driven:Reactivesystemsrelyonasynchronousmessagepassingtoensureloosecoupling,isolation,andlocationtransparency.Bydoingso,wecandelegatefailuresasmessages.Thisgivesuselasticity,loadmanagement,andflowcontrol.It'sevenpossibletoapplyback-pressure(alsoknownasthrottling)whenneeded.Allthisshouldbedonewithnon-blockingcommunicationforbetterresourceutilization.

Alright,let'susetheseprinciplespracticallyinourapplicationwithRxScala.RxScalaisjustaScalawrapperforRxJava,butitisbettertousebecauseitmakesthecodemorefunctional,andyoudon'tneedtocreateobjectssuchasAction1.

Inourapplication,wehavethreemajorresources:products,reviews,andimages.Allproductsmusthaveaprice,sowewillbuiltafullyreactivepricegeneratorwiththePlayframework,RxScala,andScalarightnow.

Sofirstofall,wewillplaywithRxScalainourPlayapplication,thenwewillcreateaseparatemicroservice,makereactivecallstothatmicroservice,andretrieveourpricesuggestionforthatservice.Alldataflowtransformationsareusingobservables.

Let'screatetheroutesforthiscontrolleratReactiveWebStore/conf/routes,asfollows:

#

#Services

#

GET/rx/pricescontrollers.RxController.prices

GET/rx/apricescontrollers.RxController.pricesAsync

Wehavetworouteshere:oneforaregularaction,andanotherforanAsyncactionthatwillreturnaScalaFuture.Let'screateanewcontrollercalledRxController.scala.ThiscontrollerneedstobelocatedatReactiveWebStore/app/controller.

Let'shavealookatRxController,whichisourreactiveRxScalasimplecontroller:

@Singleton

classRxController@Inject()(priceService:IPriceSerice)extends

Controller{

defprices=Action{implicitrequest=>

Logger.info("RXcalled.")

importExecutionContext.Implicits.global

valsourceObservable=priceService.generatePrices

valrxResult=Observable.create{sourceObservable.subscribe

}

.subscribeOn(IOScheduler())

.take(1)

.flatMap{x=>println(x);Observable.just(x)}

.toBlocking

.first

Ok("RxScalaPricesuggestedis="+rxResult)

Page 189: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

defpricesAsync=Action.async{implicitrequest=>

Logger.info("RXAsynccalled.")

importplay.api.libs.concurrent.Execution.Implicits.

defaultContext

valsourceObservable=priceService.generatePrices

valrxResult=Observable.create{sourceObservable.subscribe

}

.subscribeOn(IOScheduler())

.take(1)

.flatMap{x=>println(x);Observable.just(x)}

.toBlocking

.first

Future{Ok("RxScalaPricesugestedis="+rxResult)}

}

}

So,intheveryfirstmethodcalledprices,wereturnaregularPlayframeworkAction.WereceiveIPriceServiceviadependencyinjection.ThisIPriceServiceisareactiveservice,becauseitusesobservables.Sowecallamethod,generatePrices,whichwillreturnObservable[Double].Thiswillbeoursourceobservable,thatis,thedatasourceofourcomputation.Movingforward,wecreateanewobservablesubscribingintothesourceobservable,andthenweapplysometransformation.Forinstance,wetakejustoneelement,andthenwecanperformtransformationusingflatMap.Forthiscase,wedonotreallyapplytransformations.WeuseflatMaptosimplyprintwhatwegot,andthencontinuethechain.ThenextstepistocalltoBlocking,whichwillblockthethreaduntilthedataisback.Oncethedataisback,wegetthefirstelement,whichwillbeadouble,andwereturnOk.

Blockingsoundsbadandwedon'twantthat.Alternatively,wecanusetheasynccontrollerinthePlayframework,whichwon'tblockthethreadandreturnaFuture.Sothat'sthesecondmethod,calledpricesAsync.Herewehavesimilarobservablecode.However,intheend,wereturnaFuturewhichisnotblocking.However,wecalltoBlockingfromtheobservablethatwillblockthecall,thusmakingitthesameasthepreviousmethod.Tobeclear,Actionisnotbad.Bydefault,everythingisAsyncinPlay,becauseevenifyoudon'treturnanexplicitFuture,thePlayframeworkcreatesapromiseforyandmakesyoucodeAsync.UsingHTTP,youwillblockthethreadatsomepoint.Ifyouwanttobe100%non-blockingfromendtoend,youneedtoconsideradifferentprotocolsuchaswebsockets.

Let'stakealookattheservicenow.Thisservice,andotherservices,needtobelocatedatReactiveWebStore/apps/services.Firstwewillcreatetraittodefinetheservicebehavior.

Page 190: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IPriceService-ScalatraitAsyoucanseeinthefollowingcode,wedefinedIPriceServicewithjustoneoperation,thatis,generatePrices,whichreturnsObservable[Double].Thenextstepnowistodefinetheserviceimplementation.Thiscodeneedstobelocatedinthesameservicesfolderastheprevioustrait:

traitIPriceSerice{

defgeneratePrices:Observable[Double]

}

Page 191: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PriceService-RxScalaPriceServiceimplementationFirstwecreatePublishSubject,whichisawaytogeneratedataintoobservables.ScalahasanicewayofgeneratinginfinitesequencesusingStream.continually.Sowepassafunctionwhichgeneratesdoublerandomnumbersfrom0to1,000.Thiswillhappenforeverand,becausethisisanexpensivecomputation,werunitintoaFuture.TherightthingtodowillbetouseamethodafterStream,becausethiswillfinishthecomputation.Forthesakeoftheexercise,wewillkeepitthiswayfornow.

EachdoublerandomnumberispublishedintoPublishSubjectbytheonNextmethod.Nowlet'smovetothegeneratePricesmethod,whichusesthreeobservablestogeneratethenumberforus.Tobeclear,ofcoursewecoulddoasimplersolutionhere.However,wearedoingitthiswaytoillustratethepowerofobservables,andhowyoucanusetheminpractice.

WehaveEvenandOddobservables,bothsubscribingtoPublishSubject,sotheywillreceiveinfinitedoublenumbers.ThereisaflatMapoperationtoadd10tothenumber.Keepinmindthateverythingyoudoneedstobeinanobservable.SowhenyoudotransformationswithflatMap,youalwaysneedtoreturnanobservable.

Finally,weapplythefilterfunctiontogetonlyevennumbersontheEvenobservableandonlyoddnumbersontheOddobservable.Allthishappensinparallel.EvenandOddobservablesdonotwaitforeachother.

Thenextstepistomergebothobservables.WecreateathirdobservablethatstartsemptyandthenmergestheinfinitedoublesfromtheEvenobservablewiththeinfinitedoublesfromtheOddobservable.Nowisthetimetolimitthecomputationtoonly10numbers.Wedon'tknowhowmanyoddorhowmanyevennumberswillbetherebecauseofAsync.Ifyouwishtocontrolthenumberofoddsandevens,youneedtoapplythetakefunctiononeachobservable.

Finally,weapplyfoldLefttosumallthenumbersandgetatotal.However,whenwedothat,wegetonly90%ofthenumbers.Thislastobservableiswhatisreturnedtothecontroller.Nothingisblockedhere,andit'sallAsyncandreactive.

YoumaybewonderingwhyStream.Continuouslygeneratesdifferentvaluesallthetime.ThathappensbecauseweuseaCall-by-NamefunctioninScala.WeimportthenextDoublefunction,andpassafunctioninsteadofthevalueofthefunction:

@Singleton

classPriceServiceextendsIPriceSerice{

vardoubleInfiniteStreamSubject=PublishSubject.apply[Double]()

Future{

Stream.continually(nextDouble*1000.0).foreach{

x=>Thread.sleep(1000);

doubleInfiniteStreamSubject.onNext(x)

}

}

Page 192: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

overridedefgeneratePrices:Observable[Double]={

varobservableEven=Observable.create{

doubleInfiniteStreamSubject.subscribe}

.subscribeOn(IOScheduler())

.flatMap{x=>Observable.from(Iterable.fill(1)(x+10))

}

.filter{x=>x.toInt%2==0}

varobservableOdd=Observable.create{

doubleInfiniteStreamSubject.subscribe}

.subscribeOn(IOScheduler())

.flatMap{x=>Observable.from(Iterable.fill(1)(x+10))

}

.filter{x=>x.toInt%2!=0}

varmergeObservable=Observable

.empty

.subscribeOn(IOScheduler())

.merge(observableEven)

.merge(observableOdd)

.take(10)

.foldLeft(0.0)(_+_)

.flatMap{x=>Observable.just(x-(x*0.9))}

returnmergeObservable

}

}

WeneedtoregisterthisserviceinGuiceinModule.scalalocatedinthedefaultpackageatReactiveWebStore/app.

Page 193: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GuiceInjection-Module.scalaYourModule.scalafileshouldlooksomethinglikethis:

classModuleextendsAbstractModule{

overridedefconfigure()={

//UsethesystemclockasthedefaultimplementationofClock

bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)

//AskGuicetocreateaninstanceofApplicationTimerwhen

//the

//applicationstarts.

bind(classOf[ApplicationTimer]).asEagerSingleton()

bind(classOf[IProductService]).to(classOf[ProductService]).

asEagerSingleton()

bind(classOf[IReviewService]).to(classOf[ReviewService]).

asEagerSingleton()

bind(classOf[IImageService]).to(classOf[ImageService]).

asEagerSingleton()

bind(classOf[IPriceSerice]).to(classOf[PriceService]).

asEagerSingleton()

}

}

Inordertocompileandruntheprecedingcode,weneedtoaddanextraSBTdependency.Openbuild.sbt,andaddRxScala.Wewillalsoaddanotherdependency,whichisws,aplaylibrarytomakewebservicecalls.Wewilluseitlaterinthischapter.

Yourbuild.sbtshouldlooksomethinglikethis:

name:="""ReactiveWebStore"""

version:="1.0-SNAPSHOT"

lazyvalroot=(projectinfile(".")).enablePlugins(PlayScala)

scalaVersion:="2.11.7"

libraryDependencies++=Seq(

jdbc,

cache,

ws,

"org.scalatestplus.play"%%"scalatestplus-play"%"1.5.1"%

Test,

"com.netflix.rxjava"%"rxjava-scala"%"0.20.7"

)

resolvers+="scalaz-bintray"at

"http://dl.bintray.com/scalaz/releases"

resolvers+=DefaultMavenRepository

Nowwecancompileandrunthiscodeusingactivatorrun.

WecancallthisnewroutenowusingaCURLcall.Ifyouprefer,youcanjustopenyourbrowseranddoitthere.

Page 194: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

curl-vhttp://localhost:9000/rx/prices

curl-vhttp://localhost:9000/rx/aprices

Wewillseethefollowingresult:

Great!WehaveRxScalaworkingwiththePlayframework.Nowwewillrefactorourcodetomakeitevenmoreinteresting.SowewillcreateamicroserviceusingthePlayframework,andwewillexternalizethisrandomnumbergeneratortothemicroservice.

WeneedtocreateanewPlayframeworkapplication.Wewillpicktheoptionnumber6)play-scalaapplicationtemplate.Openyourconsole,andthentypethefollowingcommand:

$activatornewng-microservice

Youwillseethisresult:

Page 195: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Let'screatetheroutesonng-microservice.Wewon'thaveanyUIhere,sincethiswillbeamicroservice.Weneedtoaddarouteatng-microservice/conf/routes:

#Routes

#Thisfiledefinesallapplicationroutes(Higherpriorityroutes

first)

#~~~~

GET/doublecontrollers.NGServiceEndpoint.double

GET/doubles/:ncontrollers.NGServiceEndpoint.doubles(n:Int)

Nowlet'sdefinethecontroller.Thisisnotaregularcontroller,becausethisonewon'tserveUIviews.Instead,itwillserveJSONtothemicroserviceconsumers.Herewewillhavejustoneconsumer,whichwillbeReactiveWebStore.However,itispossibletohaveasmany

Page 196: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

consumersasyouwishsuchasothermicroservicesorevenmobileapplications.

Page 197: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

NGServiceEndpointForthiscontroller,wejusthavetworoutes.Theroutesaredoubleanddoubles.Thefirstroutereturnsadoublefromtheservice,andthesecondonereturnsalistofdoublesgeneratedinabatch.Forthesecondmethod,wegetalistofdoublesandtransformthatlistinJSONusingthePlayframeworkutilitylibrarycalledJson:

classNGServiceEndpoint@Inject()(service:NGContract)extends

Controller{

defdouble=Action{

Ok(service.generateDouble.toString())

}

defdoubles(n:Int)=Action{

valjson=Json.toJson(service.generateDoubleBatch(n))

Ok(json)

}

}

Thenextstepiscreatetraitforthemicroservice.ThistraitisalsotheservicecontractinServiceOrientedArchitecture(SOA)terms,thatis,thecapabilitiesthatthemicroserviceoffers.

TheNGContract.scalafileshouldlooksomethinglikethis:

traitNGContract{

defgenerateDouble:Double

defgenerateDoubleBatch(n:Int):List[Double]

}

Let'slookattheServiceimplementationofthismicroservice:

packageservices

importscala.util.Random

importscala.util.Random.nextDouble

classNGServiceImplextendsNGContract{

overridedefgenerateDouble:Double={

Stream.continually(nextDouble*1000.0)

.take(1)

}

overridedefgenerateDoubleBatch(n:Int):List[Double]={

require(n>=1,"Numbermustbebiggerthan0")

valnTimes:Option[Int]=Option(n)

nTimesmatch{

caseSome(number:Int)=>

Stream.continually(nextDouble*1000.0)

.take(n)

.toList

caseNone=>

thrownewIllegalArgumentException("Youneedprovideavalid

numberofdoublesyouwant.")

}

}

}

Page 198: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThisserviceimplementationdoesnothaveanyRxScalacode.However,itisveryfunctional.Wehavetwomethodsimplementedhere.ThemethodsaregenerateDoubleandgenerateDoubleBatch,whichreceive,throughparameters,thenumberofdoublesyouwantittogenerateforyou.Forthefirstoperation(generateDouble),weuseStream.continuallytogenerateinfiniterandomdoubles,thenwemultiplythesenumbersby1,000,andthentakejust1andreturnit.

Thesecondoperationisverysimilar.However,wehavetoaddsomevalidationstomakesurethatthenumbersofdoublearepresent.Thereareacoupleofwaystodoit.OnewayusetheassertmethodinScala.Thesecondwayisthepatternmatcher,whichisnicebecausewedon'tneedtowriteanifstatement.

ThistechniqueisverycommonintheScalacommunity.Sowecreateanoptionthattakesthenumber,andthenwepattern-matchit.Ifthereisanumberpresent,thecaseSomemethodwillbetriggered,otherwise,thecaseNonewillbecalled.

Afterthesevalidations,wecanuseStreamtogenerateasmanynumbersasrequested.Beforewerunthecode,weneedtodefinetheGuiceinjections.Thisfileislocatedinthedefaultpackageatng-microservice/app/:

classModuleextendsAbstractModule{

overridedefconfigure()={

//UsethesystemclockasthedefaultimplementationofClock

bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)

bind(classOf[NGContract]).to(classOf[NGServiceImpl]).

asEagerSingleton()

}

}

Nowitstimetocompileandrunourmicroservice.SincewealreadyhaveaplayapplicationcalledReactiveWebStorerunningonport9000,youwillhavetroubleifyousimplyrunthemicroservice.Tofixthis,weneedtorunitonadifferentport.Let'suse9090forthemicroservice.Opentheconsole,andexecutethecommand$activatorfollowedby$run9090:

ng-microservice$activator-Dsbt.task.forcegc=false

[info]Loadingglobalpluginsfrom/home/diego/.sbt/0.13/plugins

[info]Loadingprojectdefinitionfrom

/home/diego/github/diegopacheco/Book_Building_Reactive_Functional_Scala_Applic

ations/Chap4/ng-microservice/project

[info]Setcurrentprojecttong-microservice(inbuild

file:/home/diego/github/diegopacheco/Book_Building_Reactive_Functional_Scala_A

pplications/Chap4/ng-microservice/)

[ng-microservice]$run9090

---(Runningtheapplication,auto-reloadingisenabled)---

[info]p.c.s.NettyServer-ListeningforHTTPon/0:0:0:0:0:0:0:0:9090

(Serverstarted,useCtrl+Dtostopandgobacktotheconsole...)

Page 199: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Wecantestourmicroservicebycallingthetwooperationsthatwehave.Solet'sopenthewebbrowseranddoit.

Thedoublemicroservicecallathttp://localhost:9090/doublelooksasfollows:

Everytimeyoucallthisoperation,you'llseeadifferentrandomdoublenumber.Nowwecantryoutthenextoperation:theoneyoupassforthenumberofdoublesyouwant.ThisoperationwillreturnalistofdoublesintheJSONformat.

Thecallfordoubleinbatchesmicroserviceathttp://localhost:9090/doubles/100lookslikethefollowingscreenshot:

Itworks!Wehave100doubleshere.Nowthatwehaveamicroserviceworking,wecangobacktoourReactiveWebStoreandchangeourRxScalacode.Wewillcreatenewcontrollers.Wewillalsoupdatetheexistingcodeforproductstocallournewcode,andsuggestapricefortheuserontheUI,allinareactivemanner.Keepinmindthatyouneedtohaveng-microservicerunning;otherwise,ReactiveWebStorewon'tbeabletoretrievedoubles.

Page 200: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PlayframeworkandhighCPUusageIfyounoticeyourCPUusagegoinghigherthanitshould,donotworry;thereisafixforit.Actually,theissueisrelatedtoSBT.Justmakesurethat,whenyourunActivator,youpassthefollowingparameter:

$activator-Dsbt.task.forcegc=false.

GoingbacktoReactiveWebStore,let'screatenewroutes.OpenReactiveWebStore/conf/routes:

GET/rnd/double

controllers.RndDoubleGeneratorController.rndDouble

GET/rnd/callcontrollers.RndDoubleGeneratorController.rndCall

GET/rnd/rxcontrollers.RndDoubleGeneratorController.rxCall

GET/rnd/rxbat

controllers.RndDoubleGeneratorController.rxScalaCallBatch

Oncewehavethenewroutes,weneedtocreatethenewcontroller.ThiscontrollerneedstobelocatedwiththeothercontrollersatReactiveWebStore/app/controllers.

Page 201: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RndDoubleGeneratorControllerTheRndDoubleGeneratorControllerclassfileshouldlooklikethis:

@Singleton

classRndDoubleGeneratorController@Inject()(service:IRndService)

extendsController{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

defrndDouble=Action{implicitrequest=>

Ok(service.next().toString())

}

defrndCall=Action.async{implicitrequest=>

service.call().map{res=>Ok(res)}

}

defrxCall=Action{implicitrequest=>

Ok(service.rxScalaCall().toBlocking.first.toString())

}

defrxScalaCallBatch=Action{implicitrequest=>

Ok(service.rxScalaCallBatch().toBlocking.first.toString())

}

}

AllmethodsintheprecedingcontrollercalltheserviceIRndService.AlloperationsinRndServicecallng-microservice.Herewehavesomeflavorsofoperations,whichwillbecoveredingreatdetailnextwhenweexploretheserviceimplementation.

Therearesomeinterestingthingshere:forinstance,forthesecondoperationcalledrndCall,weseeAction.asyncinuse,whichmeansourcontrollerwillreturnaFuture,andthisFuturecomesfromtheservice.WealsoexecuteaMaptotransformtheresultintoanOk.

ThelastoperationcalledrxScalaCallBatchisthemostinteresting,andtheonewewillbeusingforourUI.However,youcanusetheotheroneifyouwish,sincetheyallreturndoubles,andthat'sgood.

Page 202: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IRndService.scala-ScalatraitLet'slookattheservicedefinition.Firstofall,weneedtodefineatraitfortheservicethatwilldefinetheoperationsweneed:

traitIRndService{

defnext():Double

defcall():Future[String]

defrxScalaCall():Observable[Double]

defrxScalaCallBatch():Observable[Double]

}

Page 203: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RndService.scala-RndServiceimplementationNowwecanmovetotherealserviceimplementation.ThisneedstobelocatedatReactiveWebStore/app/services:

@Singleton

classRndService@Inject()(ws:WSClient)extendsIRndService{

importplay.api.libs.concurrent.Execution.Implicits.

defaultContext

overridedefnext():Double={

valfuture=ws.url("http://localhost:9090/double").get().map

{res=>res.body.toDouble}

Await.result(future,5.seconds)

}

overridedefcall():Future[String]={

ws.url("http://localhost:9090/double").get().map

{res=>res.body}

}

overridedefrxScalaCall():Observable[Double]={

valdoubleFuture:Future[Double]=

ws.url("http://localhost:9090/double").get().map{x=>

x.body.toDouble}

Observable.from(doubleFuture)

}

//Continue...

Inordertocallourmicroservice(ng-microservice),weneedtoinjectaspecialPlayframeworklibrarycalledws,autilitylibrarytocallwebservices.Weinjectitbyaddingthecode(ws:WSClient)intotheclassdefinition.

Whenyoucallsomethingwithws,itreturnsaFuture.WeneedtohavetheFutureexecutorsinplace.That'swhytheimportdefaultContextisveryimportant,andyoucannotskipit.

Forthemethod,asyoucansee,wenextcallourmicroserviceathttp://localhost:9090/doubletogetasingledouble.Wemapthisresult,andgetthebodyoftheresult,whichwillbethedoubleitself.

Forthismethod,weuseAwait.result,whichwillblockandwaitfortheresult.Iftheresultisnotbackinfiveseconds,thiscodewillfail.

Thesecondmethodcalledcalldoesthesame,butthemaindifferenceisthatwearenotblockingtheservice;instead,wearereturningaFuturetothecontroller.

Finally,thelastmethodcalledrxScalaCalldoesthesame:itcallsourmicroserviceusingthewslibrary.However,wereturnanobservable.ObservablesaregreatbecausetheycanusedasaFuture.

Nowitistimetogocheckoutthefinaloperationandthemostinterestingone.Forthissameclass,weneedtoaddanothermethodsuchasthisone:

Page 204: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThemethodrxScalaCallBatchinRndService.scalaisasfollows:

overridedefrxScalaCallBatch():Observable[Double]={

valdoubleInfiniteStreamSubject=PublishSubject.apply[Double]()

valfuture=ws.url("http://localhost:9090/doubles/10")

.get()

.map{x=>Json.parse(x.body).as[List[Double]]}

future.onComplete{

caseSuccess(l:List[Double])=>l.foreach{e=>

doubleInfiniteStreamSubject.onNext(e)}

caseFailure(e:Exception)=>

doubleInfiniteStreamSubject.onError(e)

}

varobservableEven=Observable.create{

doubleInfiniteStreamSubject.subscribe}

.onErrorReturn{x=>2.0}

.flatMap{x=>Observable.from(Iterable.fill(1)(x+10))}

.filter{x=>x.toInt%2==0}

.flatMap{x=>println("ODD:"+x);Observable.just(x)}

varobservableOdd=Observable.create{

doubleInfiniteStreamSubject.subscribe}

.onErrorReturn{x=>1.0}

.flatMap{x=>Observable.from(Iterable.fill(1)(x+10))}

.filter{x=>x.toInt%2!=0}

.flatMap{x=>println("EVEN:"+x);Observable.just(x)}

varmergeObservable=Observable

.empty

.merge(observableEven)

.merge(observableOdd)

.take(10)

.foldLeft(0.0)(_+_)

.flatMap{x=>Observable.just(x-(x*0.9))}

mergeObservable

}

So,firstwecreatePublishSubjectinordertobeabletoproducedatafortheobservables.Thenwemakethewscalltoourmicroservice.Themaindifferencenowisthatwecallthebatchesoperationandorder10doubles.Thiscodehappensinafuture,soitisnon-blocking.

WethenusetheMapfunctiontotransformtheresult.Theng-microservicefunctionwillreturnJSON,soweneedtodeserializethisJSONintoScalaobjects.Finally,werunapatternmatcherintheFutureresult.Iftheresultisasuccess,itmeanseverythingisgood.So,foreachdouble,wepublishintotheobservablesusingPublishSubject.Iftheserviceisdownorwehaveaproblem,wepublishanerrortotheobservablesdownstream.

Nextwecreatethreeobservables:oneforoddnumbers,oneforevennumbers,andathirdonewhichwillmergetheothertwoanddoextracomputation.ThewaywedidtheconversionbetweenFutureandObservableisideal,becauseitisnon-blocking.

HerewehavecodeverysimilartowhatwehadbeforefortheRxcontroller.Themaindifferenceisthatwehaveerrorhandling,becauseng-microservicemightneverreturn,asitmaybedownorjustnotworking.Soweneedtostartworkingwithfallbacks.Goodfallbacks

Page 205: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

arekeytoerrorhandlingforReactiveapplications.Fallbacksshouldbesortofstatic;inotherwords,theyshouldnotfailatall.

Weprovidedtwofallbackmethods:onefortheOddObservableandtheotherfortheEvenObservable.ThesefallbacksaredonebysettingthemethodOnErrorReturn.Sofortheevenone,thefallbackisstaticandthevalueis2,andfortheoddonethevalueis1.Thisisgreat,becauseevenwithfailureourapplicationcontinuestowork.

Youmightrealizewearenotusingthetakefunctionthistime.Sowillthiscoderunforever?No,becauseng-microservicejustreturns10doubles.Finally,wemergetheobservablesintoasingleobservable,addallthenumbers,get90%ofthevalue,andreturnanobservable.

Page 206: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Module.scala-GuiceInjectionsNowhookthisnewserviceinGuice.Let'schangetheGuiceModule.scalalocatedatReactiveWebStore/apps/Module.scala:

classModuleextendsAbstractModule{

overridedefconfigure()={

bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)

bind(classOf[ApplicationTimer]).asEagerSingleton()

bind(classOf[IProductService]).to(classOf[ProductService]).

asEagerSingleton()

bind(classOf[IReviewService]).to(classOf[ReviewService]).

asEagerSingleton()

bind(classOf[IImageService]).to(classOf[ImageService]).

asEagerSingleton()

bind(classOf[IPriceSerice]).to(classOf[PriceService]).

asEagerSingleton()

bind(classOf[IRndService]).to(classOf[RndService]).

asEagerSingleton()

}}

NextweneedtocreateaJQueryfunctioninJavaScripttocallournewcontroller.ThisfunctionneedstobelocatedatReactiveWebStore/public/javascripts.

Thefollowingisprice.js,theJQueryfunctionthatcallsourcontroller:

/**

*ThisfunctionsloadsthepriceintheHTMLcomponent.

*/

functionloadPrice(doc){

jQuery.get("http://localhost:9000/rnd/rxbat",function(

response){

doc.getElementById("price").value=parseFloat(response)

}).fail(function(e){

alert('Wops!Wewasnotabletocall

http://localhost:9000/rnd/rxba.Error:'+e.statusText);

});

}

WejusthaveasinglefunctioncalledloadPrice,whichreceivesadocument.WeusetheJQuery.getmethodtocallourcontroller,andparsetheresponse,addingtotheHTMLtextboxcalledprice.Ifsomethinggoeswrong,wealerttheuserthatitwasnotpossibletoloadaprice.

Page 207: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

main.scala.htmlWeneedtochangeourmain.scalacodelocatedatReactiveWebStore/app/views/main.scala.htmlinordertoimportanewJavaScriptfunction:

@(title:String)(content:Html)(implicitflash:Flash)

<!DOCTYPEhtml>

<htmllang="en">

<head>

<title>@title</title>

<linkrel="shortcuticon"type="image/png"

href="@routes.Assets.at("images/favicon.png")">

<linkrel="stylesheet"media="screen"

href="@routes.Assets.at("stylesheets/main.css")">

<linkrel="stylesheet"media="screen"

href="@routes.Assets.at("stylesheets/bootstrap.min.css")">

<scriptsrc="@routes.Assets.at("javascripts/jquery-

1.9.0.min.js")"type="text/javascript"></script>

<scriptsrc="@routes.Assets.at("javascripts/bootstrap.js")"

type="text/javascript"></script>

<scriptsrc="@routes.Assets.at("javascripts/image.js")"

type="text/javascript"></script>

<scriptsrc="@routes.Assets.at("javascripts/price.js")"

type="text/javascript"></script>

</head>

<body>

<center><ahref='/'><imgheight='42'width='42'

src='@routes.Assets.at("images/rws.png")'></a>

<h3>@title</h3></center>

<divclass="container">

@alert(alertType:String)={

@flash.get(alertType).map{message=>

<divclass="alertalert-@alertType">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

@message

</div>

}

}

@alert("error")

@alert("success")

@content

<ahref="/"></a><BR>

<buttontype="submit"class="btnbtn-primary"

onclick="window.location.href='/';">

ReactiveWebStore-Home

</button>

</div>

</body>

</html>

Page 208: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

product_details.scala.htmlFinally,weneedtochangeourproductviewinordertoaddabuttontoloadthepricefromthecontroller.Let'schangetheproduct_detailsviewatReactiveWebStore/app/views/product_details.scala.html:

@(id:Option[Long],product:Form[Product])(implicitflash:Flash)

@importplay.api.i18n.Messages.Implicits._

@importplay.api.Play.current

@main("Product:"+product("name").value.getOrElse("")){

@if(product.hasErrors){

<divclass="alertalert-error">

<buttontype="button"class="close"data-

dismiss="alert">&times;</button>

Sorry!Someinformationdoesnotlookright.Couldyou

reviewitpleaseandre-submit?

</div>

}

@helper.form(action=if(id.isDefined)

routes.ProductController.update(id.get)else

routes.ProductController.insert){

@helper.inputText(product("name"),'_label->"ProductName")

@helper.inputText(product("details"),'_label->"Product

Details")

@helper.inputText(product("price"),'_label->"Price")

<divclass="form-actions">

<buttontype="button"class="btnbtn-primary"

onclick="javascript:loadPrice(document);">LoadRnd

Price</button>

<buttontype="submit"class="btnbtn-primary">

@if(id.isDefined){UpdateProduct}else{NewProduct}

</button>

</div>

}

}

Great!NowwehaveabuttonthatloadsthedatafromthecontrollerusingJQuery.YoucanrealizethatthebuttonLoadRndPricehasanonClickproperty,whichcallsourJavaScriptfunction.

Nowyouneedtoopenyourconsoleandtype$activatorruntocompileandrunthechangesaswedidtoReactiveWebStore.

Thiscommandwillgivethefollowingresult:

ReactiveWebStore$activator-Dsbt.task.forcegc=false

[info]Loadingglobalpluginsfrom/home/diego/.sbt/0.13/plugins

[info]Loadingprojectdefinitionfrom

/home/diego/github/diegopacheco/Book_Building_Reactive_Functional_Scala_Applic

ations/Chap4/ReactiveWebStore/project

[info]SetcurrentprojecttoReactiveWebStore(inbuild

file:/home/diego/github/diegopacheco/Book_Building_Reactive_Functional_Scala_A

Page 209: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

pplications/Chap4/ReactiveWebStore/)

[ReactiveWebStore]$run

---(Runningtheapplication,auto-reloadingisenabled)---

[info]p.c.s.NettyServer-ListeningforHTTPon/0:0:0:0:0:0:0:0:9000

(Serverstarted,useCtrl+Dtostopandgobacktotheconsole...)

[info]application-ApplicationTimerdemo:Startingapplicationat2016-07-

03T02:35:54.479Z.

[info]play.api.Play-Applicationstarted(Dev)

Soopenyoubrowserathttp://localhost:9000,andgototheproductpagetoseeourneedfeatureintegratedandworkinglikeacharm.Keepinmindthatyouneedtohaveng-microserviceworkinginanotherconsolewindow;otherwise,ourapplicationwillusestaticfallbacks.

Thenewproductfeaturewillbeshownathttp://localhost:9000/product/addasseeninthefollowingscreenshot:

SoifyouclickontheLoadRndPricebutton,youwillseesomethinglikethis:

Page 210: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Ifyoutakealookattheapplicationlogintheactivatorconsole,youwillseesomethingsimilartothis:

[info]application-ApplicationTimerdemo:Startingapplicationat2016-07-

03T02:35:54.479Z.

[info]play.api.Play-Applicationstarted(Dev)

[info]application-indexcalled.Products:List()

[info]application-blankcalled.

ODD:722.8017048639501

EVEN:863.8229024202085

ODD:380.5549208988492

EVEN:947.6312814830953

ODD:362.2984794191124

ODD:676.978825910585

ODD:752.7412673916701

EVEN:505.3293481709368

EVEN:849.9768444508936

EVEN:99.56583617819769

Alright,that'sit.Wehaveeverythingworking!

Page 211: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedthecoreprinciplesofreactiveapplicationguidedbytheReactiveManifesto.YoualsolearnedhowtocreatereactiveapplicationsusingRxScala.Wethenexplainedhowtocallotherinternalandexternalwebservicesusingthewslibrary.ThenyoulearnedtoserializeanddeserializeJSONusingtheJsonLibrary.Movingon,youlearnedhowtocreatesimplemicroservicesusingthePlayframeworksupport.

Inthenextchapter,wewillcontinuebuildingourapplication,andlearnhowtotestourapplicationwithJUnitandScalaTest.

Page 212: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter5.TestingYourApplicationInthechapterssofar,webootstrappedourapplicationusingActivator,developedourwebapplicationusingScalaandthePlayframework,andaddedaReactivemicroservicecallusingRxScalafordataflowcomputations.NowwewillgoaheadandentertheunittestandcontrollertestingusingtheBDDandPlayframework.

Inthischapter,wewillcoverthefollowingtopics:

UnittestingprinciplesTestingScalaapplicationswithJUnitBehavior-drivendevelopmentprinciplesTestingwithScalaTestTestingPlayframeworkapplicationswithScalaTestRunningtestsinActivator/SBT

TheneedfortestingTestisafundamentalandveryimportantpartofsoftwaredevelopment.Withouttests,wecannotbesurethatourcodeworks.Weshouldperformtestsonalmostallthecodeweproduce.Therearethingsthatdon'tmakesensefortesting,forinstance,caseclassesandclassesthatjustrepresentstructuralobjects,or,inotherwords,classeswithoutfunctions.Ifyouhavecodethatappliescomputations,transformations,andvalidations,youdefinitelywanttotestthiscodewithagoodcodecoverage,whichreferstofeaturesoranyimportantcodethatisnotjuststructural.

Testcoverageisimportant,becauseitallowsustodorefactoring(improveapplicationcodequalitybyreducingcode,creatinggenericcode,orevendeletingcode)withtrust.Thisisbecause,ifyouhavetestsandifyoudosomethingwrongbyaccident,yourtestswillletyouknow.Thisisallabouthavingshortcyclesoffeedback.Theearlierthebetter;youwanttoknowifyouhaveintroducedbugsassoon,andasclosetodevelopment,aspossible.Nobodylikestodiscoverbugsthatcouldbecaughtbyasimpletestinproduction.

Page 213: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

UnittestingprinciplesUnittestingisthesmallestunitoftestingthatyoucouldpossiblyapply.Youneedtoapplyitattheclasslevel.Soaunittestwillcoveryourclasswithallthefunctionsyouhavethere.Butholdonaminute,aclassoftenhasdependencies,andthesedependenciesmighthaveotherdependencies,sohowdoyoutestthat?Weneedtohavemocks,simpledumbobjectsthatsimulateotherclasses'behavior.Thisisanimportanttechniquetoisolatecodeandallowunittesting.

Page 214: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MakingcodetestableUnittestingissimple:basically,wecallafunctionbypassingargumentstoit,andthenwechecktheoutputtoseeifitmatchesourexpectations.Thisisalsocalledassertsorassertions.So,unittestingisaboutasserts.Sometimes,yourcodemightnotbetestable.Forinstance,let'ssayyouhaveafunctionthatreturnsaunitandhasnoparameters.Thisisverytoughtotest,becauseitimpliesthatthefunctionisfullofside-effects.IfyourememberwhatwediscussedinChapter1,IntroductiontoFP,Reactive,andScala,thisisagainstFPprinciples.So,ifwehavethiscase,weneedtorefactorthecodetomakethefunctionreturnsomething,andthenwecantestit.

Page 215: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Isolationandself-containedtestsUnittestsshouldbeself-contained,whichmeansthataunittest'sclassesshouldnotdependonanyparticularorderofexecution.Let'ssayyouhaveaunittestclasswithtwotestfunctions.So,eachtestshouldtestjustonefunctionatatime,andbothfunctionsshouldbeabletoruninanyorderwhatsoever.Otherwise,thetestswillbefragileandhardtomaintaininthelongrun.

Page 216: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

EffectivenamingEffectivenamingisessential.Thetestfunctionneedstosayexactlywhatthetestdoes.Thisisimportantbecause,whenthetestfails,itiseasiertofigureoutwhatwentwrongandwhy.Followingthesameidea,whenyoudoassertions,youshouldassertjustonethingatatime.ImaginethatyouneedtotestwhetherawebservicereturnsavalidJSON.ThisparticularJSONcouldhavetwofields:firstnameandlastname.So,youwillmakeoneassertforthenameandanotherforthelastname.Thisway,itwillbeeasiertounderstandwhatthetestdoes,andtotroubleshootwhenthetestfails.

Page 217: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

LevelsoftestingWhenweruntests,weoftendoitinlayers.Unittestingisthebasiclevel;however,thereareotherlevelssuchascontrollertests,integrationtests,UItests,End-to-Endtests,stresstests,andsomanyothers.Forthisbook,wewillcoverunittests,controllertests,andUItestsusingJunitandScalaTest,Play'sframeworksupport.

Page 218: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TestingwithJunitIfyoucomefromaJavabackground,itishighlypossiblethatyouhavealreadyworkedwithJunit.It'spossibletotestwithJunitusingtheScalaandPlayframework.However,thisisnotthebestpracticewhenwearecreatingapplicationswiththePlayframework,sinceitfavorsBehaviorDrivenDevelopment(BDD)testingwithScalaSpec.Forthischapter,wewillcoverhowtoperformallsortsoftestusingBDDandPlay.Rightnow,let'stakealookathowwecandounittestingwithJunitbeforewemovetoBDD.

@RunWith(classOf[Suite])

@Suite.SuiteClasses(Array(classOf[JunitSimpleTest]))

classJunitSimpleSuiteTest

classJunitSimpleTestextendsPlaySpecwithAssertionsForJUnit{

@TestdeftestBaseService(){

vals=newProductService

valresult=s.findAll()

assertEquals(None,result)

assertTrue(result!=null)

println("AllgoodjunitworksfinewithScalaTestandPlay")

}

}

SowhatwehaveintheprecedingcodeisaclassthatextendsPlaySpec,andaddsatraitcalledAssertionForJunit.Whydon'twehavetheclassicalJunitclasshere?BecausethePlayframeworkissetuptorunScalatests,sothisbridgeallowsustorunJunitbyScalaTestPlayframeworkconstructs.

ThenwehaveatestfunctioncalledtestBaseServer,[email protected],wecreateaninstanceofProductService,andthenwecallthefunctionfindAll.

Finally,wehaveassertionsthatwillcheckiftheresultiswhatweareexpecting.Sowedon'thaveproducts,becausewedidnotinsertthemearlier.Hence,weexpecttohaveNoneastheresponse,andtheresultshouldalsonotbenull.

Youcanrunthisinyourconsoleusingthefollowingcommand:

$activator"test-onlyJunitSimpleTest"

Youwillseetheresultshowninthenextscreenshot:

Page 219: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Asyoucansee,ourtestwasexecutedwithoutanyissues.It'salsopossibletorunthistestandanormaltestinJunitusingtheEclipseIDE.Youjustright-clickonthefileandselectRunAs:ScalaJunitTest;refertothefollowingscreenshot:

Page 220: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Behavior-DrivenDevelopment-BDDBehavior-DrivenDevelopment(BDD)isanagiledevelopmenttechnique,thatfocusesontheengagementbetweendevelopersandnon-technicalpeoplesuchasproductownersfromthebusiness.Theideaisprettysimple:usethesamelanguageasthebusinessusesinordertoextractthereasonwhythecodeyouarebuildingexistsinthefirstplace.BDDendsupminimizingthetranslationbetweentechlanguageandbusinesslanguage,creatingmoresynergyandlessnoisebetweeninformationtechnologyandbusiness.

BDDtestsdescribewhattheapplicationneedstodo,andhowitbehaves.It'sverycommontowritedownthesetestsusingpairprogramingbetweenbusinesspeopleanddevelopers.ScalaTestisaBDDframework.PlayframeworkhasagreatintegrationwithScalaTest.Let'sgetstartedwithScalaTestandPlayrightnow.

Page 221: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MyFirstPlaySpec.scala-FirstBDDwithScalaTestandthePlayframeworkTheMyFirstPlaySpec.scalaclassshouldcontainthefollowingcode:

classMyFirstPlaySpecextendsPlaySpec{

"x+1"must{

"be2ifx=1"in{

valsum=1+1

summustBe2

}

"be0ifx=-1"in{

valsum=-1+1

summustBe0

}

}

}

So,youcreateaclasscalledMyFirstPlaySpec,andweextenditfromPlaySpecinordertogetPlayframeworkScalaTestsupport.Thenwecreatetwofunctionstotestthesumoftwonumbers.Inthefirsttest,1+1shouldbe2,andinthesecond,-1+1shouldbe0.WhenweexecutemustBe,itisthesamethingasdoinganassertinJunit.ThemaindifferencehereisthatthetesthasbehaviorexplicitlyontheSpec.Nowwecanrunthetestbytypingthefollowing:

$activator"test-onlyMyFirstPlaySpec"

Youwillseethefollowingresult:

Page 222: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TestingwithPlayframeworksupportNowwewillcontinuebuildingourapplication.Let'saddBDDtestsinourapplication.Wewillstartdoingtestsforyourservices.WehavetotestProductService,ImageService,andReviewService.

YourProductServiceTestSpec.scalafileshouldcontainthefollowingcode:

classProductServiceTestSpecextendsPlaySpec{

"ProductService"must{

valservice:IProductService=newProductService

"insertaproductproperly"in{

valproduct=newmodels.Product(Some(1),

"Ball","AwesomeBasketball",19.75)

service.insert(product)

}

"updateaproduct"in{

valproduct=newmodels.Product(Some(1),

"BlueBall","AwesomeBlueBasketball",19.99)

service.update(1,product)

}

"notupdatebecausedoesnotexit"in{

intercept[RuntimeException]{

service.update(333,null)

}

}

"findtheproduct1"in{

valproduct=service.findById(1)

product.get.idmustBeSome(1)

product.get.namemustBe"BlueBall"

product.get.detailsmustBe"AwesomeBlueBasketball"

product.get.pricemustBe19.99

}

"findall"in{

valproducts=service.findAll()

products.get.lengthmustBe1

products.get(0).idmustBeSome(1)

products.get(0).namemustBe"BlueBall"

products.get(0).detailsmustBe"AwesomeBlueBasketball"

products.get(0).pricemustBe19.99

}

"findallproducts"in{

valproducts=service.findAllProducts()

products.lengthmustBe1

products(0)._1mustBe"1"

products(0)._2mustBe"BlueBall"

}

"remove1product"in{

valproduct=service.remove(1)

productmustBetrue

valoldProduct=service.findById(1)

oldProductmustBeNone

}

"notremovebecausedoesnotexist"in{

Page 223: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

intercept[RuntimeException]{

service.remove(-1)

}

}

}

}

Forthistest,wearetestingallpublicfunctionsavailableinProductService.Thetestsareprettystraightforward:wecallaspecificserviceoperation,suchasfindById,andthenwechecktheresulttomakesurethatallthedatathatissupposedtobethereispresent.

Therearescenarioswheretheserviceshouldreturnanexception,forinstance,ifyoutrytoremovesomethingthatdoesnotexist.Ifyoutakealookatthelasttestfunctioncalled"notremovebecausedoesnotexist",weshouldgetanexception.However,thereisabugintheservicecode.Runthetests,andthenyouwillseeit.

Page 224: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ProductService.scala-FIXthecodeissueThat'sthegreatthingabouttests:theyshowissuesinourcodesothatwecanfixthembeforethecodegoestoproductionandaffectstheuserexperience.Tofixthelasttest,weneedtogototheProductServiceclassandfixamethod.Thisishowwefixit:

privatedefvalidateId(id:Long):Unit={

valentry=inMemoryDB.get(id)

if(entry==null||entry.equals(None))thrownew

RuntimeException("CouldnotfindProduct:"+id)

}

Allsetnow,everythingisokay.ThePlayframeworksupportstestingforexpectedexceptionsusingtheinterceptfunctiontopasstheexpectedexception,Let'srunthetestintheconsoleusingtheactivatorcommand.

$activator"test-onlyProductServiceTestSpec"

Afterexecutingthiscommand,wegetthefollowing:

Page 225: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImageServiceTestSpec.scala-ImageServiceTestAlright,NowwecanaddtestsforImageServiceasfollows:

classImageServiceTestSpecextendsPlaySpec{

"ImageService"must{

valservice:IImageService=newImageService

"insertaimageproperly"in{

valimage=newmodels.Image(Some(1),Some(1),

"http://www.google.com.br/myimage")

service.insert(image)

}

"updateaimage"in{

valimage=newmodels.Image(Some(2),Some(1),

"http://www.google.com.br/myimage")

service.update(1,image)

}

"notupdatebecausedoesnotexist"in{

intercept[RuntimeException]{

service.update(333,null)

}

}

"findtheimage1"in{

valimage=service.findById(1)

image.get.idmustBeSome(1)

image.get.productIdmustBeSome(1)

image.get.urlmustBe"http://www.google.com.br/myimage"

}

"findall"in{

valreviews=service.findAll()

reviews.get.lengthmustBe1

reviews.get(0).idmustBeSome(1)

reviews.get(0).productIdmustBeSome(1)

reviews.get(0).urlmustBe"http://www.google.com.br/myimage"

}

"remove1image"in{

valimage=service.remove(1)

imagemustBetrue

valoldImage=service.findById(1)

oldImagemustBeNone

}

"notremovebecausedoesnotexist"in{

intercept[RuntimeException]{

service.remove(-1)

}

}

}

}

SothesearetheBDDtestsforImageService.Wehavecoveredalltheavailablefunctionsontheservice.LikeintheProductServiceclass,wealsohavetestsforunfortunatescenarioswhereweexpectexceptionstohappen.

Sometimes,weneedtocallmorethanonefunctiontotestaspecificfunctionoraspecifictest

Page 226: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

case.Forexample,in"remove1image",wefirstdeleteanimage.Ourtestcasechecksforanimagethatdoesnotexist.Let'srunthetestsontheActivatorconsole.

$activator"test-onlyImageServiceTestSpec"

Thefollowingresultwillbeobtained:

Page 227: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReviewServiceTestSpec.scala-ReviewServicetestNext,weneedaddtestsforthereviewservice.Herewego.

classReviewServiceTestSpecextendsPlaySpec{

"ReviewService"must{

valservice:IReviewService=newReviewService

"insertareviewproperly"in{

valreview=new

models.Review(Some(1),Some(1),"diegopacheco",

"TestingisCool")

service.insert(review)

}

"updateareview"in{

valreview=newmodels.Review(Some(1),Some(1),

"diegopacheco","TestingsosoCool")

service.update(1,review)

}

"notupdatebecausedoesnotexist"in{

intercept[RuntimeException]{

service.update(333,null)

}

}

"findthereview1"in{

valreview=service.findById(1)

review.get.idmustBeSome(1)

review.get.authormustBe"diegopacheco"

review.get.commentmustBe"TestingsosoCool"

review.get.productIdmustBeSome(1)

}

"findall"in{

valreviews=service.findAll()

reviews.get.lengthmustBe1

reviews.get(0).idmustBeSome(1)

reviews.get(0).authormustBe"diegopacheco"

reviews.get(0).commentmustBe"TestingsosoCool"

reviews.get(0).productIdmustBeSome(1)

}

"remove1review"in{

valreview=service.remove(1)

reviewmustBetrue

valoldReview=service.findById(1)

oldReviewmustBeNone

}

"notremovebecausedoesnotexist"in{

intercept[RuntimeException]{

service.remove(-1)

}

}

}

}

Alright,wehavetestsforthereviewservice.Wecanrunthemnow.

$activator"test-onlyReviewServiceTestSpec"

Page 228: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Hereistheoutput:

Page 229: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TestingroutesThePlayframeworkallowsustotestroutesaswell.Thisisgood,becauseasourapplicationgrowsandwerefactorthecode,wecanbe100%surethatourroutesarefunctioning.Routetestingcouldbeeasilyconfusedwithcontrollertesting.Themaindifferenceisthat,withroutingtesting,weshouldtestifweareabletoreachtheroutesandthat'sit.Afterroutetesting,wewillcovercontrollertestingindetail.

Page 230: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RoutesTestingSpec.scala-PlayframeworkroutetestingYourRoutesTestingSpec.scalafileshouldcontainthefollowingcode:

classRoutesTestingSpecextendsPlaySpecwithOneAppPerTest{

"RootController"should{

"routetoindexpage"in{

valresult=route(app,FakeRequest(GET,"/")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("WelcometoReactive

WebStore")

}

}

"ProductController"should{

"routetoindexpage"in{

valresult=route(app,FakeRequest(GET,"/product")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("Product")

}

"routetonewproductpage"in{

valresult=route(app,FakeRequest(GET,

"/product/add")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("Product")

}

"routetoproduct1detailspagepage"in{

try{

route(app,FakeRequest(GET,"/product/details/1")).get

}catch{

casee:Exception=>Unit

}

}

}

"ReviewController"should{

"routetoindexpage"in{

valresult=route(app,FakeRequest(GET,"/review")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("Review")

}

"routetonewreviewpage"in{

valresult=route(app,FakeRequest(GET,

"/review/add")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("review")

}

"routetoreview1detailspagepage"in{

try{

route(app,FakeRequest(GET,"/review/details/1")).get

}catch{

casee:Exception=>Unit

Page 231: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

}

}

"ImageController"should{

"routetoindexpage"in{

valresult=route(app,FakeRequest(GET,"/image")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("Image")

}

"routetonewimagepage"in{

valresult=route(app,FakeRequest

(GET,"/image/add")).get

status(result)mustBeOK

contentType(result)mustBeSome("text/html")

contentAsString(result)mustinclude("image")

}

"routetoimage1detailspagepage"in{

try{

route(app,FakeRequest(GET,"/image/details/1")).get

}catch{

casee:Exception=>Unit

}

}

}

}

Soherewehavetestsforallourmaincontrollers,whichareroot,product,review,andimage.RootControlleristhecontrollerofthemainpagewhenyouvisithttp://localhost:9000.

ThereisaspecialhelperfunctioncalledrouteinthePlayframework,whichhelpsustotestroutes.ThenweuseFakeRequestpassthepathtotheroute.It'spossibletotestthestatuscodeandcontenttypeofthepagetowhichtherouterroutedourrequest.

Forproduct,image,andreviewcontrollers,youcanseewearetryingtocallanitemthatdoesnotexist.That'swhywehavethetry...catch,becauseweexpecttohaveanexceptionhappeningthere.

$activator"test-onlyRoutesTestingSpec"

Executingthisprecedingcommandproducesthefollowingresult:

Page 232: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About
Page 233: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ControllertestingWedidunittests,wedidroutetests,andnowisthetimetoaddcontrollertests.Controllertestsaresimilartoroutestests,buttheyarenotthesame.Forinstance,ourcontrolleralwaysrespondtoUIpages,soweexpectedtocreatespecificHTMLpagesbasedoneachmethod.ThePlayframeworkhasintegrationwithSelenium,whichisatestingframeworkforUIs,andacontrollersthatallowsyoutosimulatewebbrowsers,andyoucandoprettymuchthesameasyouwouldbyclickingonthepageslikearealuser.

Solet'sgetstarted.First,wewillstartwithRndDoubleGeneratorControllerTestSpec.

Page 234: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RndDoubleGeneratorControllerTestSpec.scala-RndDoubleGeneratorControllertestsTheRndDoubleGeneratorControllerTestSpec.scalafileshouldcontainthefollowingcode:

classRndDoubleGeneratorControllerTestSpec

extendsPlaySpec

withOneServerPerSuitewithOneBrowserPerSuitewithHtmlUnitFactory

{

valinjector=newGuiceApplicationBuilder()

.injector

valws:WSClient=injector.instanceOf(classOf[WSClient])

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

"Assumingng-microserviceisdownrxnumbershouldbe"must{

"work"in{

valfuture=ws.url(s"http://localhost:${port}/rnd/rxbat")

.get().map{res=>res.body}

valresponse=Await.result(future,15.seconds)

responsemustBe"2.3000000000000007"

}

}

}

Thisclasshassomeinterestingthings.Forinstance,weinjectWSClientusingGoogleGuicewithGuiceApplicationBuilder.Secondly,weassumethattheng-microservicewecreatedinthepreviouschapterisdown,sowecanpredicttheresponsecomingfromthefallback.

WecallthecontrollerusingWSClient,andthenwemaptheresponsetoreturnthebodycontentasastring.SothiswillbeanAsyncFuture,andinordertogettheresult,weuseAwaittowaitfivesecondsfortheresponsetocomeback.Oncetheresponseisback,wemakesuretheresultis2.3.Iftheresultdoesnotcomebackin15s,thetestwillfail.Runthefollowingcommand:

$activator"test-onlyRndDoubleGeneratorControllerTestSpec"

Nowyouwillseethefollowingresult:

Page 235: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Allright,nowwehaveacontrollertestfullyworkingusingGuiceinjectionsandtheWSClientPlayframeworklibrary.Let'snowmakecontrollertestsfortheproduct,image,andreviewcontrollers.

Page 236: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntegrationSpec.scalaWecantestourmainpagetocheckifitisokay.Thisisaverysimpletest,andgetsyoureadyforthenexttests.So,herewego.

classIntegrationSpecextendsPlaySpecwithOneServerPerTestwith

OneBrowserPerTestwithHtmlUnitFactory{

"Application"should{

"workfromwithinabrowser"in{

goto("http://localhost:"+port)

pageSourcemustinclude("WelcometoReactiveWebStore")

}

}

}

Thistestisveryeasy.Wejustcallthemainpage,andwecheckifitcontainsthetextWelcometoReactiveWebStore.Let'srunit.

$activator"test-onlyIntegrationSpec"

Theresultafterrunningthistestisshowninthefollowingimage:

Page 237: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ProductControllerTestSpec.scalaNowwewilllookattheproductcontrollertestspec:

classProductControllerTestSpecextendsPlaySpecwith

OneServerPerSuitewithOneBrowserPerSuitewithHtmlUnitFactory{

"ProductController"should{

"insertanewproductshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

}

"detailsfromtheproduct1shouldbeok"in{

goTo(s"http://localhost:${port}/product/details/1")

textField("name").valuemustBe"BlueBall"

textField("details").valuemustBe"BlueBallisaAwesome

andsimpleproduct"

textField("price").valuemustBe"17.55"

}

"updateproduct1shouldbeok"in{

goTo(s"http://localhost:${port}/product/details/1")

textField("name").value="BlueBall2"

textField("details").value="BlueBallisaAwesomeand

simpleproduct2"

textField("price").value="17.66"

submit()

goTo(s"http://localhost:${port}/product/details/1")

textField("name").valuemustBe"BlueBall2"

textField("details").valuemustBe"BlueBallisaAwesome

andsimpleproduct2"

textField("price").valuemustBe"17.66"

}

"deleteaproductshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/product")

clickonid("btnDelete")

}

}

}

So,fortheproductcontroller,wesimulateawebbrowserusingSeleniumPlay'sframeworksupport.Wetestbasiccontrollerfunctionalitysuchasinsertinganewproduct,detailsfora

Page 238: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

specificproduct,andupdatingandremovingaproduct.

Forinsert,wegotothenewproductformusinggoTo.Weuse$portasavariable.WedothisbecausethePlayframeworkwillbootuptheapplicationforus,butwedon'tknowinwhichport.Soweneedtousethisvariableinordertogettotheproductcontroller.

Thenweclickoneachtextfieldusingtheclickfunction,andweentervaluesusingtheenterfunction.Afterfillinginthewholeform,wesubmititusingthesubmitfunction.

Fordetails,wejustgototheproductdetailspage,andwecheckifthetextfieldshavethevaluesthatweareexpecting.WedothatusingthetextField.valuefunction.

Inordertochecktheproductupdatefunction,weneedtofirstupdatetheproductdefinition,andthengotodetailstoseeifthevalueswechangedarethere.

Finally,wetestthedeletefunction.Forthisfunction,weneedtoclickonabutton.WeneedtosettheIDofthebuttoninordertohavethisworking.WeneedtodoasmallrefactoringinourUItohavetheIDthere.

Page 239: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

product_index.scala.htmlYourproduct_index.scala.htmlfileshouldcontainthefollowingcode:

@(products:Seq[Product])(implicitflash:Flash)

@main("Products"){

@if(!products.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>Name</th>

<th>Details</th>

<th>Price</th>

<th></th>

</tr>

@for(product<-products){

<tr>

<td><ahref="@routes.ProductController.details

(product.id.get)">@product.name</a></td>

<td>@product.details</td>

<td>@product.price</td>

<td><formmethod="post"action=

"@routes.ProductController.remove(product.id.get)">

<buttonid="btnDelete"name="btnDelete"class="btn

btn-link"type="submit"><iclass="icon-

trash"></i>Delete</button>

</form></td>

</tr>

}

</table>

}

<p><ahref="@routes.ProductController.blank"

class="btnbtn-success"><iclass="icon-plusicon-white">

</i>AddProduct</a></p>

}

Allset.NowwecanrunourtestsonActivatorsusingtheconsole.

$activator"test-onlyProductControllerTestSpec"

Thisprecedingcommandshowstheresultinthefollowingscreenshot:

Page 240: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Sincethistestrunstheapplicationforrealandcallsthecontrollersimulatingthewebbrowser,thistestcouldtakesometime.Nowit'stimetomovetotheImageControllertests.

Page 241: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImageControllerTestSpec.scalaYourproduct_index.scala.htmlshouldcontainthefollowingcode:

classImageControllerTestSpecextendsPlaySpecwithOneServerPerSuitewith

OneBrowserPerSuitewith

HtmlUnitFactory{

"ImageController"should{

"insertanewimageshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/image/add")

singleSel("productId").value="1"

clickonid("url")

enter("http://myimage.com/img.jpg")

submit()

}

"detailsfromtheimage1shouldbeok"in{

goTo(s"http://localhost:${port}/image/details/1")

textField("url").valuemustBe"http://myimage.com/img.jpg"

}

"updateimage1shouldbeok"in{

goTo(s"http://localhost:${port}/image/details/1")

textField("url").value="http://myimage.com/img2.jpg"

submit()

goTo(s"http://localhost:${port}/image/details/1")

textField("url").valuemustBe"http://myimage.com/img2.jpg"

}

"deleteaimageshouldbeok"in{

goTo(s"http://localhost:${port}/image/add")

singleSel("productId").value="1"

clickonid("url")

enter("http://myimage.com/img.jpg")

submit()

goTo(s"http://localhost:${port}/image")

clickonid("btnDelete")

}

}

}

Firstofall,weneedtogototheproductcontrollertoinsertaproduct;otherwise,wecannotdoimageoperations,sincetheyallneedaproductID.

Page 242: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

image_index.scala.htmlYourimage_index.scala.htmlfileshouldcontainthefollowingcode:

@(images:Seq[Image])(implicitflash:Flash)

@main("Images"){

@if(!images.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>ProductID</th>

<th>URL</th>

<th></th>

</tr>

@for(image<-images){

<tr>

<td><ahref="@routes.ImageController.details

(image.id.get)">@image.id</a></td>

<td>@image.productId</td>

<td>@image.url</td>

<td><formmethod="post"action=

"@routes.ImageController.remove(image.id.get)">

<buttonid="btnDelete"name="btnDelete"class="btn

btn-link"type="submit"><iclass="icon-

trash"></i>Delete</button></form>

</td>

</tr>

}

</table>

}

<p><ahref="@routes.ImageController.blank"

class="btnbtn-success"><iclass="icon-plusicon-white">

</i>AddImage</a></p>

}

Allset.NowwecanruntheImageControllertests.

$activator"test-onlyImageControllerTestSpec"

Theresultisshowninthefollowingscreenshot:

Page 243: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImageControllerhaspassedallitstests.NowwewillmovetotheReviewControllertests.

Page 244: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReviewControllerTestSpec.scalaYourReviewControllerTestSpec.scalafileshouldcontainthefollowingcode:

classReviewControllerTestSpecextendsPlaySpecwithOneServerPerSuitewith

OneBrowserPerSuite

withHtmlUnitFactory{

"ReviewController"should{

"insertanewreviewshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/review/add")

singleSel("productId").value="1"

clickonid("author")

enter("diegopacheco")

clickonid("comment")

enter("Testsareamazing!")

submit()

}

"detailsfromthereview1shouldbeok"in{

goTo(s"http://localhost:${port}/review/details/1")

textField("author").valuemustBe"diegopacheco"

textField("comment").valuemustBe"Testsareamazing!"

}

"updatereview1shouldbeok"in{

goTo(s"http://localhost:${port}/review/details/1")

textField("author").value="diegopacheco2"

textField("comment").value="Testsareamazing2!"

submit()

goTo(s"http://localhost:${port}/review/details/1")

textField("author").valuemustBe"diegopacheco2"

textField("comment").valuemustBe"Testsareamazing2!"

}

"deleteareviewshouldbeok"in{

goTo(s"http://localhost:${port}/review/add")

singleSel("productId").value="1"

clickonid("author")

enter("diegopacheco")

clickonid("comment")

enter("Testsareamazing!")

submit()

goTo(s"http://localhost:${port}/review")

clickonid("btnDelete")

}

}

Firstofall,weneedtogototheproductcontrollertoinsertaproduct;otherwise,wecannotdoimageoperations,sincetheyallneedaproductID.

Page 245: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Forinsert,wegotothenewproductformusinggoto.Weuse$portasavariable.WedothisbecausethePlayframeworkwillbootuptheapplicationforus,butwedon'tknowonwhichport,soweneedtousethisvariableinordertogettotheproductcontroller.

Thenweclickoneachtextfieldusingtheclickfunction,andweentervaluesusingtheenterfunction.Afterfillinginthewholeform,wesubmititusingthesubmitfunction.

Fordetails,wejustgototheproductdetailspageandcheckifthetextfieldshavethevaluesthatweexpect.WedothatusingthetextField.valuefunction.

Inordertochecktheproductupdatefunction,weneedtofirstupdatetheproductdefinition,andthengotothedetailstoseeifthevalueswechangedarethere.

Finally,wetestthedeletefunction.Forthisfunction,weneedtoclickonabutton.WeneedtosettheIDofthebuttoninordertohavethisworking.ThenwedoasmallrefactoringinourUItohavetheIDthere.

Page 246: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

review_index.scala.htmlYourreview_index.scala.htmlfileshouldcontainthefollowingcode:

@(reviews:Seq[Review])(implicitflash:Flash)

@main("Reviews"){

@if(!reviews.isEmpty){

<tableclass="tabletable-striped">

<tr>

<th>ProductId</th>

<th>Author</th>

<th>Comment</th>

<th></th>

</tr>

@for(review<-reviews){

<tr>

<td><ahref="@routes.ReviewController.

details(review.id.get)">@review.productId</a></td>

<td>@review.author</td>

<td>@review.comment</td>

<td>

<formmethod="post"action="@routes.

ReviewController.remove(review.id.get)">

<buttonid="btnDelete"name="btnDelete"

class="btnbtn-link"type="submit"><iclass="icon-

trash"></i>Delete</button>

</form>

</td>

</tr>

}

</table>

}

<p><ahref="@routes.ReviewController.blank"class="btnbtn-

success"><iclass="icon-plusicon-white"></i>AddReview</a></p>

}

Finally,wecanrunthetestsontheconsole.

$activator"test-onlyReviewControllerTestSpec"

Thetestwillshowthefollowingoutput:

Page 247: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Alright,ReviewControllerhaspassedallourtests.

It'saverygoodpracticetohavethetestsseparatedbytype.However,ifyouwant,youcouldmixallthetestssuchasunittesting,controllertesting,androutetestinginonesinglefile.

Page 248: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ApplicationSpec.scalaYourApplicationSpec.scalashouldhavethefollowingcode:

classApplicationSpecextendsPlaySpecwithOneAppPerTest{

"Routes"should{

"send404onabadrequest"in{

route(app,FakeRequest(GET,"/boum")).map(status(_))mustBe

Some(NOT_FOUND)

}

}

"HomeController"should{

"rendertheindexpage"in{

valhome=route(app,FakeRequest(GET,"/")).get

status(home)mustBeOK

contentType(home)mustBeSome("text/html")

contentAsString(home)mustinclude("WelcometoReactiveWeb

Store")

}

}

"RndController"should{

"returnarandomnumber"in{

//Assumingng-microserviceisdownotherwisewillfail.

contentAsString(route(app,FakeRequest(GET,

"/rnd/rxbat")).get)mustBe"2.3000000000000007"

}

}

}

Wecanrunthesemixedtests,andtheywillallpass.

$activator"test-onlyApplicationSpec"

Youwillseethisresult:

OK,wearealmostdone.Wejustneedtoaddsometestsforthemicroservicecalledng-

Page 249: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

microservice,whichwecreatedinChapter4,DevelopingReactiveBackingServices.

Page 250: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

NGServiceImplTestSpec.scalaYourNGServiceImplTestSpec.scalafileshouldhavethefollowingcode:

classNGServiceImplTestSpecextendsPlaySpec{

"TheNGServiceImpl"must{

"GenerateaRamdonnumber"in{

valservice:NGContract=newNGServiceImpl

valdouble=service.generateDouble

assert(double>=1)

}

"Generatealistof3Ramdonnumbers"in{

valservice:NGContract=newNGServiceImpl

valdoubles=service.generateDoubleBatch(3)

doubles.sizemustBe3

assert(doubles(0)>=1)

assert(doubles(1)>=1)

assert(doubles(2)>=1)

}

}

}

Sohere,intheprecedingcode,wehavetwomethodstotestthetwooperationsthatwehaveinourmicroservice.Firstwegenerateonedouble,andthenweaskforalistofthreedoubles.Asyoucansee,wejustcheckifwegetapositivedoublebackfromtheservice,andthat'sit.Sincetheresultisnotpredictable,thisisagoodwaytotestit.Sometimes,evenwhentheresultispredictable,youwanttestslikethis.Why?Becauseitmakesthetestsmorereliable,andoften,whenweusetoomanyhardcorevalues.Thevaluescouldbechangingandbreakingourtests,andthat'snotcool.Let'srunitontheconsole.

$activator"test-onlyNGServiceImplTestSpec"

Hereistheresultthatweget:

Nowlet'smoveontothecontroller,anddosomecontrollertesting.

Page 251: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

NGServiceEndpointControllerTest.scalaYourNGServiceEndpointControllerTest.scalafileshouldcontainthefollowingcode:

classNGServiceEndpointControllerTestextendsPlaySpecwith

OneServerPerSuitewith

OneBrowserPerSuitewithHtmlUnitFactory{

valinjector=newGuiceApplicationBuilder()

.injector

valws:WSClient=injector.instanceOf(classOf[WSClient])

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

"NGServiceEndpointController"must{

"returnasingledouble"in{

valfuture=ws.url(s"http://localhost:${port}/double")

.get().map{res=>res.body}

valresponse=Await.result(future,15.seconds)

responsemustnotbeempty

assert(newjava.lang.Double(response)>=1)

}

"returnalistof3doubles"in{

valfuture=ws.url(s"http://localhost:${port}/doubles/3")

.get().map{res=>res.body}

valresponse=Await.result(future,15.seconds)

responsemust(notbeemptyandinclude("[")and

include("]"))

}

}

}

HerewehavetoinjecttheWSClientlibrarysowecancallthecontroller.Thiscontrollerhastwomethodsliketheservicewetestedbefore.ThesecondmethodreturnsaJSONstructure.Thenwecheckfor"["and"]"tomakesurethatthearrayispresent,sincethisisalistofthreenumbers.

Weusetheassertfunctiontochecktheresponsefromthecontroller,andtobe100%surethateverythingisokay.Let'srunit.

$activator"test-onlyNGServiceEndpointControllerTest"

Refertothefollowingscreenshottoseethetestresult:

Page 252: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Great!Wehavecoveredprettymucheverything.

Inthischapter,weranallkindsoftests.Wealwaysusedthecommand$activator"test-onlyxxx";thereasonforthisistosavetime.However,itisverycommontorunalltests.Youcandothatinbothprojects;wehavetojusttype$activatortest.

Whenrunningallthetestsintheng-microserviceproject,wegettheresultshowninthefollowingscreenshot:

Ontheotherhand,runningallthetestsintheReactiveWebStoreprojectgivestheresultshowninthenextscreenshot:

Page 253: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About
Page 254: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtodotests.WeaddedseveraltestsforyourScalaandPlayframeworkprojects.Youalsolearnedaboutunittestingprinciples,testingScalaapplicationswithJUnit,BDDprinciples,testingwithScalaTest,testingPlayframeworkapplicationswithScalaTest,andrunningtestsinActivator/SBT.

Inthenextchapter,youwilllearnmoreaboutpersistenceusingSlick,whichisreactive.Wewillalsochangeourtestsalittlebitinordertoworkwithadatabase.

Page 255: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter6.PersistencewithSlickInthepreviouschapters,youlearnedtobootstrapyourapplicationusingActivator,wedevelopedourwebapplicationusingScalaandPlayframework,andweaddedReactivemicroservicescallsusingRxScalafordataflowcomputations.WeperformedunittestsandcontrollertestingusingtheBDDandPlayframeworks.

Inthischapter,youwilllearnhowtoachieverelationaldatabasepersistence.Sofar,wehaveourapplicationupandrunning.However,weareusingin-memorypersistencewithHashMap.Nowwewillupgradeourapplicationtouseproperpersistence.Inordertoachievethis,wewilluseareactivedatabasepersistenceframeworkcalledSlick.

Inthischapter,wewillcoverthefollowingtopics:

PrinciplesofdatabasepersistencewithSlickWorkingwithFunctionalRelationalMappinginourapplicationCreatingQuerieswithSQLsupportImprovingcodewithAsyncdatabaseoperations

Wewilldosomerefactoringinourapplication.Step-by-step,wewillcreateallthetablesandpersistenceclassesthatweneedtohaveSlickworkingwithourPlayframeworkapplication.Testswillberefactoredaswellinordertotesttheapplicationlogic,andskipthedatabasepersistencepart.

Page 256: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntroducingtheSlickframeworkScalaLanguageIntegratedConnectionKit(Slick)isaScalamodernframework,whichallowsworkingwithdatausingabstractionsthatareverysimilartoScalacollections.YoucanwritedatabasequeriesinbothSQLandScalacode.WritingScalacodeinsteadofSQLisbetter,becauseweleveragethecompiler,andhence,thisapproachislesserror-prone.Also,itbecomeseasiertomaintainthecode,sincethecompilerwillhelpyoubypointingoutwherethecodebreaks,ifithappens.

SlickisaFunctionalRelationalMapping(FRM)library.ThoseofyouwhocomefromaJavabackground,andarefamiliarwithObjectRelationalMapping(ORM)frameworkssuchasHibernate,willseethatSlickhassimilarconcepts.Basically,youcreateaScalaclass,whichwillexplicitlymaptoarelationaltable.SlickFRMideasareinspiredbyMicrosoft'sLINQframework.

Slickisreactivebydesign,andworksinanasynchronousnon-blockingIOmodel.UsingSlickyouhavethefollowingadvantages:

Resilience:AcommonissueisthataheavyloadontheDBandapplicationcreatesmorethreads,andmakesthesituationworse.Slickcanfixthisproblem,becauseitqueuesdatabaseoperationsintheDB.EfficientResourcesutilization:Slickcanbetunedforparallelismintermsofthenumberofactivejobsandsuspendeddatabasesessions.SlickalsohasacleandemarcationbetweenI/OandCPU-intensivecode.

Page 257: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MySQLsetupWewilluseSlickwithMySQL5.6.However,SlicksupportsotherrelationaldatabaseslikeOracle,SQLServer,DB2,andPostgres.Firstofall,weneedtoinstallMySQLinourmachine.Opentheconsole,andperformthefollowingsteps(forUbuntuLinux,otherOS(Windows/Mac),anddistros,checkouthttp://dev.mysql.com/downloads/mysql/):

$sudoapt-getinstallmysql-server-y

$mysql--version

$servicemysqlstatus

Afterinstallationwithapt-get,whenyouruntheothertwocommands,youshouldseeanoutputlikethis:

MySQLInstallation

OncetheinstallationisdoneandtheMySQLserverisupandrunning,wecanmoveonandcreatethedatabase.Inordertogetthis,wewillneedtoopentheMySQLconsole.Fordevelopmentreasons,Ididnotputapasswordforroot.However,forproduction,itisstronglyrecommendedthatyoudouseastrongpassword.

Executethefollowingcommand:

$mysql-uroot-p

Thiswillgiveoutputasfollows:

Page 258: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MySQLConsole

OnceyouentertheMySQLconsole,youcancreatethedatabase.WewillcreateadatabasenamedRWS_DBusingthefollowingcommand:

$CREATEDATABASERWS_DB;

Youwillseethefollowingresult:

Youcantype$SHOWDATABASES;inordertogetalistofalltheavailabledatabasesinMySQL.Allset,wehaveourdatabaseupandrunning.

Page 259: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringSlickinourPlayframeworkappFirst,weneedtoadddependenciestothebuild.sbtfile.WewillneedtoremoveorcommentalibrarycalledJDBCandaddtheplay-slicklibrariesandMySQLjavadriver.

Thisisdoneasfollows:

name:="""ReactiveWebStore"""

version:="1.0-SNAPSHOT"

lazyvalroot=(projectinfile(".")).enablePlugins(PlayScala)

scalaVersion:="2.11.7"

libraryDependencies++=Seq(//jdbc,

cache,

ws,

"org.scalatestplus.play"%%"scalatestplus-play"%"1.5.1"%

Test,

"com.netflix.rxjava"%"rxjava-scala"%"0.20.7",

"com.typesafe.play"%%"play-slick"%"2.0.0",

"com.typesafe.play"%%"play-slick-evolutions"%"2.0.0",

"mysql"%"mysql-connector-java"%"6.0.3"

)

resolvers+="scalaz-bintray"at"http://dl.bintray.com/scalaz/releases"

resolvers+=DefaultMavenRepository

Asyoucanseeintheprecedingcode,wecommentouttheJDBClibrary,andaddthreenewdependencies:

"com.typesafe.play"%%"play-slick"%"2.0.0",

"com.typesafe.play"%%"play-slick-evolutions"%"2.0.0",

"mysql"%"mysql-connector-java"%"6.0.3"

Youcangototheconsoleandrunthecommands$activator,$reload,and$compileinordertogetSBTtodownloadallthenewdependencies.

Page 260: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfigurethedatabaseconnectionSlickneedstobeconfiguredtoaccesstheMySQLdatabasethatwecreated.UnderthefolderReactiveWebStore/conf,weneedtoedittheapplication.conffileandaddthedatabaseconnectionURLandsettingsasfollows:

#Defaultdatabaseconfiguration

slick.dbs.default.driver="slick.driver.MySQLDriver$"

slick.dbs.default.db.driver="com.mysql.cj.jdbc.Driver"

slick.dbs.default.db.url="jdbc:mysql://127.0.0.1:3306/RWS_DB?

useUnicode=true&useJDBCCompliantTimezoneShift=

true&useLegacyDatetimeCode=false&serverTimezone=UTC"

slick.dbs.default.db.user="root"

slick.dbs.default.db.password=""

Page 261: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

FPMMappingThenextstepistocreateFPMmappingbetweenourScalacodeandMySQLtablesunderReactiveWebStore/app,wewillcreateanewpackagecalleddao.DAOstandsforDatabaseAccessObject(DAO),andisawell-knownOOpattern.SowewillcreatesomeDAOclasseshere.Firstwewilldefineabasetrait,whichwilldefinethebehaviorandcodecapabilityforeachofourdaopackages.

WewillstartwithBaseDao.scala:

packagedao

importslick.lifted.TableQuery

importscala.concurrent.Future

/**

*Definesbasedaostructureeverydaoshouldhave.

*/

traitBaseDao[T]{

deftoTable():TableQuery[_]

deffindAll():Future[Seq[T]]

defremove(id:Long):Future[Int]

definsert(o:T):Future[Unit]

defupdate(o:T):Future[Unit]

deffindById(id:Long):Future[Option[T]]

}

Wewillhavethreedaopackages:ProductDao,ImageDao,andReviewDao.Eachdaowillbeabletoperformanoperation,butoveradifferentMySQLtable.Accordingtothetraitwejustdefined,wewillbeabletodothefollowing:

findAll:Findalldataforaspecifictableremove:DeleteaniteminatablebyIDinsert:Addnewdatatoatableupdate:UpdatedatainatablefindbyId:Getaspecificrecordinatable,filteredbyIDtoTable:ReturnthetableFRMmappingforthatdao.

Page 262: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ProductDaoWewillstartwithhavingalookatProductDao.scala:

packagedao

traitIProductDaoextendsBaseDao[Product]{

deffindAll():Future[Seq[Product]]

deffindById(id:Long):Future[Option[Product]]

defremove(id:Long):Future[Int]

definsert(p:Product):Future[Unit]

defupdate(p2:Product):Future[Unit]

}

classProductDao@Inject()(protectedvaldbConfigProvider:

DatabaseConfigProvider)

extends

HasDatabaseConfigProvider[JdbcProfile]withIProductDao{

importdriver.api._

classProductTable(tag:Tag)extendsTable[Product](tag,

models.ProductDef.toTable){

defid=column[Option[Long]]("ID",O.PrimaryKey)

defname=column[String]("NAME")

defdetails=column[String]("DETAILS")

defprice=column[BigDecimal]("PRICE")

def*=(id,name,details,price)<>(Product.tupled,

Product.unapply_)

}

overridedeftoTable=TableQuery[ProductTable]

privatevalProducts=toTable()

overridedeffindAll():Future[Seq[Product]]=

db.run(Products.result)

overridedeffindById(id:Long):Future[Option[Product]]=

db.run(Products.filter(_.id===id).result.headOption)

overridedefremove(id:Long):Future[Int]=

db.run(Products.filter(_.id===id).delete)

overridedefinsert(p:Product):Future[Unit]=db.run(Products

+=p).map{_=>()}

overridedefupdate(p2:Product)=Future[Unit]{

db.run(

Products.filter(_.id===p2.id)

.map(p=>(p.name,p.details,p.price))

.update((p2.name,p2.details,p2.price))

)

}

}

ThisisthedaoimplementationforProduct.Wehavelotsofnewconceptshere,solet'stakealookateachstep,oneatatime.Asyoucansee,atraitcalledIProductDaowhichextendsfromBaseDaousinggenericstospecifythemodelProduct.

ThistraitisimportantfordependencyinjectionusingGuice.Wewillhavetwodaoimplementationsforeachmodel:oneimplementationusingSlickandMySQL,andtheotherusingourpreviousinMemorydatabasefortestingpurpose.

Page 263: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThereisaclasstherecalledProductDaowhichisthedaoimplementationusingSlick.WeneedGuicetoinjectaclasshere,calledDatabaseConfigProvider,whichwillbeusedtoperformthedatabaseoperations.ProductDaoalsoneedstoextendHasDatabaseConfigProvider[JdbcProfile]toworkwiththedatabase.

Wealsoneedtoimportthedriverapiviathefollowingcommand:

Importdriver.api._

ThenextstepistocreateFRMmappingwithaclasscalledProductTable,whichextendstablepassingthemodel,whichinourcaseisaproduct.YoualsoneedtoannouncethenameoftheMySQLtable.Inordertogetthetablename,weuseacompanionobject,whichweneedtocreatearoundourmodels.WedoitthiswayinordertoavoidduplicatingtheMySQLtablenameeverywhere.

IntheProductTabletable,youcanseesomefunctionssuchasid,name,price,anddetails.Thesearetheexactnameofthefieldsofmodel.Product.However,wehavetoaddthemappingtotheMySQLtableontherightside.WedoitusingafunctioncalledcolumnwherewepassthetypeandtheexactMySQLfieldname.

Finally,weneedtorunaspecialprojectionfunctioncalled*topassallthefieldsonthemodel,whicharebeingmappedtotherelationaldatabase.

Nowwecanmovetothedaooperations.Asyoucansee,allthefunctionsusedb.runtoperformdataaccess.Thisisgreatbecause,asyoucanrealize,theyreturnaFuturesothedaowon'tbeblocking,andyoucandosomethingelse,forinstance,moredatabaseoperations,pre-optimizations,andvalidations.

OncewehaveaProductTabletable,wecancreateaSlickTableQuerywithittoperformdatabaseoperationsasiftheyareScalafunctions.Inordertolistalltheavailableproducts,wejustusethiscommand:

db.run(Products.result)

Itisassimpleasthat.ThiscodewillreturnaFuture[Seq[Products]].WecanalsofilterbyIDusingthis:

db.run(Products.filter(_.id===id).result.headOption)

So,first_.idistheidonthedatabase,andidistheonethatcomesbyparameter.Aftergettingtheresult,youcanseethatwecalledanotherfunctioncalledheadOption,whichmakessurethatwegettheresultasanoption.Thisisagreatpatterntorelyon,sincethedatamightnotbethereonthetable,andweavoidgettingNoSuchElementException.

Removingaproductisfairlytrivialaswell.Wejustusethefollowing:

db.run(Products.filter(_.id===id).delete)

Page 264: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThisprecedingcodereturnsFuture[Int],countingthenumberofitemsthatweredeleted.IftherecordIDisnotfoundinthedatabase,theresultwillbe0.Weexpectittobealways1,sincewearegoingtodeletebyID.However,theAPIisgeneric,andif,let'ssay,youdeletebynameoranotherfield,youmighthavemultipledeletes.That'swhyitisanIntandnotaBoolean.

Insertingdataiseasytoo;wejustgivethefollowingcommand:

db.run(Products+=p).map{_=>()}

Asyoucansee,itisaverysimplemapfunctionasifwewereaddinganelementtoalist.Thiscodereturnsunit,whichmeansnothing.However,westillhaveaFuture,sothiscodeisnotblocking.

Toperformanupdate,thereisalittlebitmorecode,butitisstillsimpleattheendoftheday.

db.run(

Products.filter(_.id===p2.id)

.map(p=>(p.name,p.details,p.price))

.update((p2.name,p2.details,p2.price))

)

Firstweneedtoapplyafiltertoselecttherecordsthatwewillupdate.WepasstheID,becausewejustwanttoupdateasinglerecord.Thenweneedtoapplyamapfunctiontopickthefieldsthatwewanttoupdate;finally,weperformtheupdate,passingthenewvaluestotheupdatefunction.

Let'stakealookatthecompanionobjectfortheproductmodel.

HereisthecodeforModels.Product.scala:

objectProductDef{

deftoTable:String="Product"

}

Asyoucansee,thisisasimplehelpercompanionobjecttoholdtheMySQLtablename.

Page 265: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReviewDAOWearedonewithProductDao,andnowweneedtomovetothereviewmodelandcreatedaoforreviews.Wewillperformstepssimilartotheoneswedidfortheproduct.

ReviewDao.scalaisasfollows:

packagedao

traitIReviewDaoextendsBaseDao[Review]{

deffindAll():Future[Seq[Review]]

deffindById(id:Long):Future[Option[Review]]

defremove(id:Long):Future[Int]

definsert(p:Review):Future[Unit]

defupdate(p2:Review):Future[Unit]

}

classReviewDao@Inject()(protectedvaldbConfigProvider:

DatabaseConfigProvider)

extendsHasDatabaseConfigProvider[JdbcProfile]withIReviewDao{

importdriver.api._

classReviewTable(tag:Tag)extendsTable[Review](tag,

models.ReviewDef.toTable){

defid=column[Option[Long]]("ID",O.PrimaryKey)

defproductId=column[Option[Long]]("PRODUCT_ID")

defauthor=column[String]("AUTHOR")

defcomment=column[String]("COMMENT")

def*=(id,productId,author,comment)<>(Review.tupled,

Review.unapply_)

}

overridedeftoTable=TableQuery[ReviewTable]

privatevalReviews=toTable()

overridedeffindAll():Future[Seq[Review]]=

db.run(Reviews.result)

overridedeffindById(id:Long):Future[Option[Review]]=

db.run(Reviews.filter(_.id===id).result.headOption)

overridedefremove(id:Long):Future[Int]=

db.run(Reviews.filter(_.id===id).delete)

overridedefinsert(r:Review):Future[Unit]=

db.run(Reviews+=r).map{_=>()}

overridedefupdate(r2:Review)=Future[Unit]{

db.run(

Reviews.filter(_.id===r2.id)

.map(i=>(i.productId,i.author,i.comment))

.update((r2.productId,r2.author,r2.comment))

)

}

}

Intheprecedingcode,wehavetheelementsthatwesawinProductDao.ThereisaninterfacefordaocalledIReviewDao,whichextendsBaseDaousingthereviewmodel.WehavetheReviewDaoimplementationwiththeReviewTableFRMmapping.Wealsohaveacompanionobjectforthereviewmodel.

Review.scalaisasfollows:

Page 266: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

objectReviewDef{

deftoTable:String="Review"

}

Page 267: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImageDaoNowweneedtomovetoourlastdao,ImageDao.LikeProductDaoandReviewDao,wewillgothroughthesameideasandconceptsasimplementationtoo.

WewillnowlookatImageDao.scala:

packagedao

traitIImageDaoextendsBaseDao[Image]{

deffindAll():Future[Seq[Image]]

deffindById(id:Long):Future[Option[Image]]

defremove(id:Long):Future[Int]

definsert(p:Image):Future[Unit]

defupdate(p2:Image):Future[Unit]

}

classImageDao@Inject()(protectedvaldbConfigProvider:

DatabaseConfigProvider)

extends

HasDatabaseConfigProvider[JdbcProfile]

withIImageDao{

importdriver.api._

classImageTable(tag:Tag)extendsTable[Image](tag,

models.ImageDef.toTable){

defid=column[Option[Long]]("ID",O.PrimaryKey)

defproductId=column[Option[Long]]("PRODUCT_ID")

defurl=column[String]("URL")

def*=(id,productId,url)<>(Image.tupled,Image.unapply

_)

}

overridedeftoTable=TableQuery[ImageTable]

privatevalImages=toTable()

overridedeffindAll():Future[Seq[Image]]=

db.run(Images.result)

overridedeffindById(id:Long):Future[Option[Image]]=

db.run(Images.filter(_.id===id).result.headOption)

overridedefremove(id:Long):Future[Int]=

db.run(Images.filter(_.id===id).delete)

overridedefinsert(i:Image):Future[Unit]=

db.run(Images+=i).map{_=>()}

overridedefupdate(i2:Image)=Future[Unit]

{db.run(

Images.filter(_.id===i2.id)

.map(i=>(i.productId,i.url))

.update((i2.productId,i2.url))

)}

}

Wealsoneedtohaveacompanionobjecthelperfortheimage.

Image.scalaisasfollows:

objectImageDef{

deftoTable:String="Image"

}

Page 268: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SlickevolutionsSlickwon'tcreatethetableforus,unlesswecreateanevolution.SlickkeepstrackofthedatabasestateandcreatesandappliesSQLcommandsforus.EvolutionsneedbelocatedatReactiveWebStore/conf/evolutions/default,wheredefaultisthenameofthedatabaseweconfiguredinapplication.conf.EvolutionsneedtobenamedinasequentialwaysothatwecanpreserveorderandSlickcankeeptrackofthechanges.Rightnow,wewillcreateanevolutionforProductDao,becauseweneedaproducttable.

Thecodewillbeasfollowswiththename1.sql:

#---!Ups

CREATETABLEProduct(IDINTNOTNULLAUTO_INCREMENT,NAMEVARCHAR(100)NOT

NULL,DETAILSVARCHAR(250),PRICEDOUBLENOTNULL,PRIMARYKEY(ID));

#---!Downs

#droptable"Product";

Weneedevolutionsforthreviewandimageaswell.Soweneedtocreate2.sqlfortheimageand3.sqlforthereview.

Thecodewillbeasfollowsfor2.sql:

#---!Ups

CREATETABLEImage(IDINTNOTNULLAUTO_INCREMENT,PRODUCT_IDINTNOTNULL,URL

VARCHAR(250),PRIMARYKEY(ID));

#---!Downs

#droptable"Product";

Thecodewillbeasfollowswiththename3.sql:

#---!Ups

CREATETABLEReview(IDINTNOTNULLAUTO_INCREMENT,PRODUCT_IDINTNOT

NULL,AUTHORVARCHAR(250),COMMENTVARCHAR(250),PRIMARYKEY(ID));

#---!Downs

#droptable"Review";

Page 269: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RefactoringservicesWeneedtochangethedefaultbasetraitforourdaopackagestoreturnFuturesnow.

Let'sstartwithBaseServices.scala:

packageservices

importscala.concurrent.Future

traitBaseService[A]{

definsert(a:A):Future[Unit]

defupdate(id:Long,a:A):Future[Unit]

defremove(id:Long):Future[Int]

deffindById(id:Long):Future[Option[A]]

deffindAll():Future[Option[Seq[A]]]

}

Thislastimplementationreflectswhat'shappeninginthedaopackages.Nowwecanmovetotheservicesimplementation,andproceedwithourrefactoring.

NextweseeProductService.scala:

packageservices

traitIProductServiceextendsBaseService[Product]{

definsert(product:Product):Future[Unit]

defupdate(id:Long,product:Product):Future[Unit]

defremove(id:Long):Future[Int]

deffindById(id:Long):Future[Option[Product]]

deffindAll():Future[Option[Seq[Product]]]

deffindAllProducts():Seq[(String,String)]

}

@Singleton

classProductService

@Inject()(dao:IProductDao)

extendsIProductService{

importplay.api.libs.concurrent.Execution.Implicits.

defaultContext

definsert(product:Product):Future[Unit]={

dao.insert(product);

}

defupdate(id:Long,product:Product):Future[Unit]={

product.id=Option(id.toInt)

dao.update(product)

}

defremove(id:Long):Future[Int]={

dao.remove(id)

}

deffindById(id:Long):Future[Option[Product]]={

dao.findById(id)

}

deffindAll():Future[Option[Seq[Product]]]={

dao.findAll().map{x=>Option(x)}

}

privatedefvalidateId(id:Long):Unit={

Page 270: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

valfuture=findById(id)

valentry=Awaits.get(5,future)

if(entry==null||entry.equals(None))thrownew

RuntimeException("CouldnotfindProduct:"+id)

}

deffindAllProducts():Seq[(String,String)]={

valfuture=this.findAll()

valresult=Awaits.get(5,future)

valproducts:Seq[(String,String)]=result

.getOrElse(Seq(Product(Some(0),"","",0)))

.toSeq

.map{product=>(product.id.get.toString,product.name)}

returnproducts

}

}

Thereareacoupleofchangeshere.First,weinjectanIProductDao,andletGuicefigureouttherightinjectionthatweneedtobeabletotestwithouroldin-memoryHashMapimplementation,whichwillbecoveredlaterinthischapter.

Thechangesinvolvenewfunctionsignatures,usingAwaits,andusingSeqinsteadofList.

Let'smovetoonReviewService.scalanow.

packageservices

traitIReviewServiceextendsBaseService[Review]{

definsert(review:Review):Future[Unit]

defupdate(id:Long,review:Review):Future[Unit]

defremove(id:Long):Future[Int]

deffindById(id:Long):Future[Option[Review]]

deffindAll():Future[Option[Seq[Review]]]

}

@Singleton

classReviewService@Inject()(dao:IReviewDao)

extendsIReviewService{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

definsert(review:Review):Future[Unit]={

dao.insert(review);}

defupdate(id:Long,review:Review):Future[Unit]={

review.id=Option(id.toInt)

dao.update(review)

}

defremove(id:Long):Future[Int]={

dao.remove(id)

}

deffindById(id:Long):Future[Option[Review]]={

dao.findById(id)

}

deffindAll():Future[Option[Seq[Review]]]={

dao.findAll().map{x=>Option(x)}

}

privatedefvalidateId(id:Long):Unit={

valfuture=findById(id)

valentry=Awaits.get(5,future)

Page 271: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

if(entry==null||entry.equals(None))thrownew

RuntimeException("CouldnotfindReview:"+id)

}

}

Intheprecedingcode,wehavethesamekindofchangesthatwemadefortheproduct.Let'smovetoImageService.scala,whichisourlastservice.

packageservices

traitIImageServiceextendsBaseService[Image]{

definsert(image:Image):Future[Unit]

defupdate(id:Long,image:Image):Future[Unit]

defremove(id:Long):Future[Int]

deffindById(id:Long):Future[Option[Image]]

deffindAll():Future[Option[Seq[Image]]]

}

@Singleton

classImageService@Inject()(dao:IImageDao)

extendsIImageService{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

definsert(image:Image):Future[Unit]={

dao.insert(image)

}

defupdate(id:Long,image:Image):Future[Unit]={

image.id=Option(id.toInt)

dao.update(image)

}

defremove(id:Long):Future[Int]={

dao.remove(id)

}

deffindById(id:Long):Future[Option[Image]]={

dao.findById(id)

}

deffindAll():Future[Option[Seq[Image]]]={

dao.findAll().map{x=>Option(x)}

}

privatedefvalidateId(id:Long):Unit={

valfuture=findById(id)

valentry=Awaits.get(5,future)

if(entry==null||entry.equals(None))thrownew

RuntimeException("CouldnotfindImage:"+id)

}

}

Wehaverefactoredallservicestousethenewdaopackagesimplementation.Nowthenextstepisthemovetothecontrollers.

Page 272: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RefactoringcontrollersNowwehaveallthedaopackagesimplementedwiththerespectivedatabaseevolutions.However,ourservicesexpectedadifferentcontract,sincewewereusinganin-memorydatabasebefore.Let'srefactortheproductcontroller:

packagecontrollers

@Singleton

classProductController@Inject()(valmessagesApi:MessagesApi,val

service:IProductService)

extendsControllerwithI18nSupport{

valproductForm:Form[Product]=Form(

mapping(

"id"->optional(longNumber),

"name"->nonEmptyText,

"details"->text,

"price"->bigDecimal

)(models.Product.apply)(models.Product.unapply)

)

defindex=Action{implicitrequest=>

valproducts=Awaits.get(5,service.findAll())

.getOrElse(Seq())

Logger.info("indexcalled.Products:"+products)

Ok(views.html.product_index(products))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.product_details(None,productForm))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valproduct=Awaits.get(5,service.findById(id)).get

Ok(views.html.product_details(Some(id),

productForm.fill(product)))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

productForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.product_details(None,form))

},

product=>{

service.insert(product)

Redirect(routes.ProductController.index).

flashing("success"->Messages("success.insert",

"newproductcreated"))

}

)

}

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

productForm.bindFromRequest.fold(

form=>{

Ok(views.html.product_details(Some(id),

Page 273: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

form)).flashing("error"->"Fixtheerrors!")

},

product=>{

service.update(id,product)

Redirect(routes.ProductController.index).flashing

("success"->Messages("success.update",product.name))

}

)

}

defremove(id:Long)=Action{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

valresult=Awaits.get(5,service.findById(id))

result.map{product=>

service.remove(id)

Redirect(routes.ProductController.index).flashing("success"

->Messages("success.delete",product.name))

}.getOrElse(NotFound)

}

}

Therearetwobigchangesintheprecedingcodedespitethenewfunctionsignatures.First,weuseautilityfunctioncalledgetfromaclasscalledAwaits.Thisisneededsothatwewaitfortheresulttocomebackfromthedatabase.Second,whenweflashtheresult,wenolongershowtheid,wejustdisplayatextmessage.Let'stakealookattheAwaitsimplementationinUtils.Awaits.scala,whichisasfollows:

packageutils

objectAwaits{

defget[T](sec:Int,f:Future[T]):T={

Await.result[T](f,secseconds)

}

}

AwaitsisjustasimpleutilityclassthatwaitsforaperiodoftimetogetaFutureresult.WeneedtoaddsometweaksinReviewControllerandImageControlleraswell.

WewillfirstexploreReviewController.scala:

packagecontrollers

@Singleton

classReviewController@Inject()

(valmessagesApi:MessagesApi,valproductService:IProductService,

valservice:IReviewService)

extendsControllerwithI18nSupport{

valreviewForm:Form[Review]=Form(

mapping(

"id"->optional(longNumber),

"productId"->optional(longNumber),

"author"->nonEmptyText,

"comment"->nonEmptyText

)(models.Review.apply)(models.Review.unapply)

)

defindex=Action{implicitrequest=>

Page 274: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

valreviews=Awaits.get(5,service.findAll()).getOrElse(Seq())

Logger.info("indexcalled.Reviews:"+reviews)

Ok(views.html.review_index(reviews))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.review_details(None,

reviewForm,productService.findAllProducts))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valreview=Awaits.get(5,service.findById(id)).get

Ok(views.html.review_details(Some(id),

reviewForm.fill(review),productService.findAllProducts))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

reviewForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.review_details(None,

form,productService.findAllProducts))

},

review=>{

if(review.productId==null||review.productId.isEmpty){

Redirect(routes.ReviewController.blank).flashing("error"

->"ProductIDCannotbeNull!")

}else{

Logger.info("Review:"+review)

if(review.productId==null||

review.productId.getOrElse(0)==0)thrownew

IllegalArgumentException("ProductIdCannotBeNull")

service.insert(review)

Redirect(routes.ReviewController.index).

flashing("success"->Messages("success.insert",

"newReview"))

}

}

)

}

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

reviewForm.bindFromRequest.fold(

form=>{

Ok(views.html.review_details(Some(id),

form,productService.findAllProducts)).

flashing("error"->"Fixtheerrors!")

},

review=>{

service.update(id,review)

Redirect(routes.ReviewController.index).

flashing("success"->Messages("success.update",

review.productId))

}

)

}

defremove(id:Long)=Action{

Page 275: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

valresult=Awaits.get(5,service.findById(id))

result.map{review=>

service.remove(id)

Redirect(routes.ReviewController.index).flashing("success"-

>Messages("success.delete",review.productId))

}.getOrElse(NotFound)

}

}

ForReviewController,wehavemadethesamechangesthatwedidfortheproduct,thatis,theuseofAwaitsandlabelsonflashreturns.

Let'smoveontothefinalcontroller:ImageController.scala.

packagecontrollers

@Singleton

classImageController@Inject()

(valmessagesApi:MessagesApi,

valproductService:IProductService,

valservice:IImageService)

extendsControllerwithI18nSupport{

valimageForm:Form[Image]=Form(

mapping(

"id"->optional(longNumber),

"productId"->optional(longNumber),

"url"->text

)(models.Image.apply)(models.Image.unapply)

)

defindex=Action{implicitrequest=>

valimages=Awaits.get(5,service.findAll()).getOrElse(Seq())

Logger.info("indexcalled.Images:"+images)

Ok(views.html.image_index(images))

}

defblank=Action{implicitrequest=>

Logger.info("blankcalled.")

Ok(views.html.image_details(None,

imageForm,productService.findAllProducts))

}

defdetails(id:Long)=Action{implicitrequest=>

Logger.info("detailscalled.id:"+id)

valimage=Awaits.get(5,service.findById(id)).get

Ok(views.html.image_details(Some(id),

imageForm.fill(image),productService.findAllProducts))

}

definsert()=Action{implicitrequest=>

Logger.info("insertcalled.")

imageForm.bindFromRequest.fold(

form=>{

BadRequest(views.html.image_details(None,form,

productService.findAllProducts))

},

image=>{

if(image.productId==null||

Page 276: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

image.productId.getOrElse(0)==0){

Redirect(routes.ImageController.blank).flashing

("error"->"ProductIDCannotbeNull!")

}else{

if(image.url==null||"".equals(image.url))

image.url="/assets/images/default_product.png"

service.insert(image)

Redirect(routes.ImageController.index).

flashing("success"->Messages("success.insert",

"newimage"))

}

}

)

}

defupdate(id:Long)=Action{implicitrequest=>

Logger.info("updatedcalled.id:"+id)

imageForm.bindFromRequest.fold(

form=>{

Ok(views.html.image_details(Some(id),form,

null)).flashing("error"->"Fixtheerrors!")

},

image=>{

service.update(id,image)

Redirect(routes.ImageController.index).flashing

("success"->Messages("success.update",image.id))

}

)

}

defremove(id:Long)=Action{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

valresult=Awaits.get(5,service.findById(id))

result.map{image=>

service.remove(id)

Redirect(routes.ImageController.index).flashing("success"

->Messages("success.delete",image.id))

}.getOrElse(NotFound)

}

}

Page 277: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringDAOpackagesinGuiceWeneedtoconfiguretheinjectionsforthethreenewdaopackagesthatwecreated.SowetoneedtoaddthreelinesinthefileModule.scala.PleaseopenthefileinyourIDE,andaddthefollowingcontent:

bind(classOf[IProductDao]).to(classOf[ProductDao]).asEagerSingleton()

bind(classOf[IImageDao]).to(classOf[ImageDao]).asEagerSingleton()

bind(classOf[IReviewDao]).to(classOf[ReviewDao]).asEagerSingleton

Thewholefile,Module.scala,shouldlooklikethis:

/**

*ThisclassisaGuicemodulethattellsGuicehowtobindseveral

*differenttypes.ThisGuicemoduleiscreatedwhenthePlay

*applicationstarts.

*Playwillautomaticallyuseanyclasscalled`Module`thatisin

*therootpackage.Youcancreatemodulesinotherlocationsby

*adding`play.modules.enabled`settingstothe`application.conf`

*configurationfile.

*/

classModuleextendsAbstractModule{

overridedefconfigure()={

//UsethesystemclockasthedefaultimplementationofClock

bind(classOf[Clock]).toInstance(Clock.systemDefaultZone)

//AskGuicetocreateaninstanceofApplicationTimerwhen

//theapplicationstarts.

bind(classOf[ApplicationTimer]).asEagerSingleton()

bind(classOf[IProductService]).

to(classOf[ProductService]).asEagerSingleton()

bind(classOf[IReviewService]).

to(classOf[ReviewService]).asEagerSingleton()

bind(classOf[IImageService]).

to(classOf[ImageService]).asEagerSingleton()

bind(classOf[IPriceSerice]).

to(classOf[PriceService]).asEagerSingleton()

bind(classOf[IRndService]).

to(classOf[RndService]).asEagerSingleton()

bind(classOf[IProductDao]).

to(classOf[ProductDao]).asEagerSingleton()

bind(classOf[IImageDao]).

to(classOf[ImageDao]).asEagerSingleton()

bind(classOf[IReviewDao]).

to(classOf[ReviewDao]).asEagerSingleton()

}

}

Page 278: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RefactoringtestsAsyoumightexpect,mosttestsarenolongerworking.Wewillneedtoperformsomerefactoringhereaswell.Wewillrefactorourformerdaotomakeitgeneric,anditwillbeusedinintegrationtests(end-to-endtests).

Sincewewillcreateagenericdaosysteminmemoryforend-to-endtestingpurposes,weneedtochangeourmodelsalittlebit.First,weneedtocreateabasetraitforallthemodels.Thisisneededsowecantreatourmodelsasequals.

Let'shavealookatmodels.BaseModel.scala:

packagemodels

traitBaseModel{

defgetId:Option[Long]

defsetId(id:Option[Long]):Unit

}

Wealsoneedtomakeallourmodelsimplementthisnewtrait.SowewillneedtochangetheScalacodefortheproduct,image,andreview.Thisisverytrivial:wejustaddagetterandasetterfortheidfield.Youcanalsousescala.bean.BeanPropertyinsteadofwritingonebyyourself.

Yourmodels.Product.scalafileshouldlooksomethinglikethis:

packagemodels

caseclassProduct

(varid:Option[Long],

varname:String,

vardetails:String,

varprice:BigDecimal)

extendsBaseModel{

overridedeftoString:String={

"Product{id:"+id.getOrElse(0)+",name:"+name+",

details:"+details+",price:"+price+"}"

}

overridedefgetId:Option[Long]=id

overridedefsetId(id:Option[Long]):Unit=this.id=id

}

objectProductDef{

deftoTable:String="Product"

}

Asyoucanseeintheprecedingcode,weextendtheBaseModelmethod,andimplementgetIdandsetId.Weneedtodothesameforthereviewandimagemodels.

Yourmodels.Review.scalafileshouldlooklikethis:

packagemodels

caseclassReview

Page 279: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

(varid:Option[Long],

varproductId:Option[Long],

varauthor:String,

varcomment:String)

extendsBaseModel{

overridedeftoString:String={

"Review{id:"+id+",productId:"+

productId.getOrElse(0)+",author:"+author+",comment:"

+comment+"}"

}

overridedefgetId:Option[Long]=id

overridedefsetId(id:Option[Long]):Unit=this.id=id

}

objectReviewDef{

deftoTable:String="Review"

}

Nowwemoveontothelastmodel.WeneedtoimplementitinImage.scala.

Yourmodels.Image.scalafileshouldlooklikethis:

packagemodels

caseclassImage

(varid:Option[Long],

varproductId:Option[Long],

varurl:String)

extendsBaseModel{

overridedeftoString:String={

"Image{productId:"+productId.getOrElse(0)+",url:"+

url+"}"

}

overridedefgetId:Option[Long]=id

overridedefsetId(id:Option[Long]):Unit=this.id=id

}

objectImageDef{

deftoTable:String="Image"

}

Page 280: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GenericmocksNowwehaveallthatweneedtocreateagenericmockimplementationandmockallthedaopackages.UnderthelocationReactiveWebStore/test/,wewillcreateapackagecalledmocks,andcreateacall,GenericMockedDao.

YourGenericMockedDao.scalafileshouldlooklikethis:

packagemocks

importmodels.BaseModel

classGenericMockedDao[T<:BaseModel]{

importjava.util.concurrent.atomic.AtomicLong

importscala.collection.mutable.HashMap

importscala.concurrent._

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

varinMemoryDB=newHashMap[Long,T]

varidCounter=newAtomicLong(0)

deffindAll():Future[Seq[T]]={

Future{

if(inMemoryDB.isEmpty)Seq()

inMemoryDB.values.toSeq

}

}

deffindById(id:Long):Future[Option[T]]={

Future{

inMemoryDB.get(id)

}

}

defremove(id:Long):Future[Int]={

Future{

validateId(id)

inMemoryDB.remove(id)

1

}

}

definsert(t:T):Future[Unit]={

Future{

valid=idCounter.incrementAndGet();

t.setId(Some(id))

inMemoryDB.put(id,t)

Unit

}

}

defupdate(t:T):Future[Unit]={

Future{

validateId(t.getId.get)

inMemoryDB.put(t.getId.get,t)

Unit

}

}

privatedefvalidateId(id:Long):Unit={

valentry=inMemoryDB.get(id)

if(entry==null||entry.equals(None))thrownew

Page 281: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RuntimeException("CouldnotfindProduct:"+id)

}

}

SotheGenericMockedDaocallexpectstheGenericparameter,whichcouldbeanyclassextendingfromBaseModel.Thenweuseanin-memoryHashMapimplementationandacountertosimulatedatabaseoperations.WerunalltheoperationsinsideFutures,sowedon'tbreakthenewsignaturethecodeisexpecting.NowwecancreatethreeMockedDaosforeachmodelweneed:product,review,andimage.

Yourmocks.ProductMockedDao.scalafileshouldlooklikethis:

packagemocks

classProductMockedDaoextendsIProductDao{

valdao:GenericMockedDao[Product]=new

GenericMockedDao[Product]()

overridedeffindAll():Future[Seq[Product]]={

dao.findAll()

}

overridedeffindById(id:Long):Future[Option[Product]]={

dao.findById(id)

}

overridedefremove(id:Long):Future[Int]={

dao.remove(id)

}

overridedefinsert(p:Product):Future[Unit]={

dao.insert(p)

}

overridedefupdate(p2:Product):Future[Unit]={

dao.update(p2)

}

overridedeftoTable:TableQuery[_]={

null

}

}

Asyoucanseehere,weimplementtheIProdutDaotrait,andwedelegatealloperationstogenericMockedDao.Sinceeverythingisin-memory,wedon'tneedtoimplementthetoTablefunction.Weneedtodothesameforthereviewandimage.

Yourmocks.ReviewMockedDao.scalafileshouldlooklikethis:

packagemocks

classReviewMockedDaoextendsIReviewDao{

valdao:GenericMockedDao[Review]=new

GenericMockedDao[Review]()

overridedeffindAll():Future[Seq[Review]]={

dao.findAll()

}

overridedeffindById(id:Long):Future[Option[Review]]={

dao.findById(id)

}

overridedefremove(id:Long):Future[Int]={

Page 282: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

dao.remove(id)

}

overridedefinsert(p:Review):Future[Unit]={

dao.insert(p)

}

overridedefupdate(p2:Review):Future[Unit]={

dao.update(p2)

}

overridedeftoTable:TableQuery[_]={

null

}

}

Exactlylikeproduct,wedelegatealloperationstoGenericMockedDao.Nowlet'smovetothelastone,theimage,andthenwecanfixthetests.

Yourmocks.ImageMockedDao.scalafileshouldlooklikethis:

packagemocks

classImageMockedDaoextendsIImageDao{

valdao:GenericMockedDao[Image]=newGenericMockedDao[Image]()

overridedeffindAll():Future[Seq[Image]]={

dao.findAll()

}

overridedeffindById(id:Long):Future[Option[Image]]={

dao.findById(id)

}

overridedefremove(id:Long):Future[Int]={

dao.remove(id)

}

overridedefinsert(p:Image):Future[Unit]={

dao.insert(p)

}

overridedefupdate(p2:Image):Future[Unit]={

dao.update(p2)

}

overridedeftoTable:TableQuery[_]={

null

}

}

Okay,wehaveallthemocksthatweneedfornow.Wecanmoveontofixthetestspecs.Weneedtofixservicestestsandcontrollertest.Servicestestswillusemocks.Controllerstests,however,willusetherealdatabaseimplementation.Weneedtouseotherutilityclassesforcontrollertests.Locatedinthetestsourcefolder,weneedtocreateapackagecalledutils.

Yourutils.DBCleaner.scalafileshouldlooklikethis:

packageutils

objectDBCleaner{

valpool=Executors.newCachedThreadPool()

implicitvalec=ExecutionContext.fromExecutorService(pool)

defcleanUp():Unit={

Class.forName("com.mysql.cj.jdbc.Driver")

Page 283: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

valdb=Database.forURL

("jdbc:mysql://127.0.0.1:3306/RWS_DB?useUnicode=

true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=

false&serverTimezone=UTC",

"root","")

try{

Await.result(

db.run(

DBIO.seq(

sqlu"""DELETEFROMProduct;""",

sqlu"""DELETEFROMImage;""",

sqlu"""DELETEFROMReview;""",

sqlu"""ALTERTABLEProductAUTO_INCREMENT=1""",

sqlu"""ALTERTABLEImageAUTO_INCREMENT=1""",

sqlu"""ALTERTABLEReviewAUTO_INCREMENT=1"""

)

)

,20seconds)

}catch{

casee:Exception=>Unit

}

}

}

DBCleanerwillconnecttotherealdatabaseandperformdeletestatementstocleanupalltabledata.Afterdeletingalldatainthetables,wealsoresetthesequenceinthedatabase;otherwise,ourtestswillnothavethepredictabilityweneedtodoassertions.

Asyoucanseeindb.run,wecanuseDBIO.seq,whichallowsustoexecutemultipleinstructionsonthedatabase.HerewearenotusingScalacode.WeareusingpureSQLstatements,sinceweneedtouseveryspecificMySQLfunctionstoresetthesequences.

Ifyouneed,youcoulduseallthesefunctionsinyourapplication.Thisisusefulifyouneedtouseaspecificdatabasefunction,ifyouhaveaverycomplexquery,orsometimes,becausethereisaperformanceissue.

MostfixeswedonowcenteraroundusingAwaitstowaitfortheFutureresult,andalsousingournewmocks.Forthecontrollertest,weneedtocalltheDBCleanerfunctionaswell.

Page 284: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ServicetestsNowwewillcreatetestsforservicestotestthem.Solet'sgetstarted.

YourProductServiceTestSpec.scalafileshouldlooklikethis:

classProductServiceTestSpecextendsPlaySpec{

"ProductService"must{

valservice:IProductService=newProductService(new

ProductMockedDao)

"insertaproductproperly"in{

valproduct=newmodels.Product(Some(1),"Ball","Awesome

Basketball",19.75)

service.insert(product)

}

"updateaproduct"in{

valproduct=newmodels.Product(Some(1),"Blue

Ball","AwesomeBlueBasketball",19.99)

service.update(1,product)

}

"notupdatebecausedoesnotexit"in{

intercept[RuntimeException]{

service.update(333,null)

}

}

"findtheproduct1"in{

valproduct=Awaits.get(5,service.findById(1))

product.get.idmustBeSome(1)

product.get.namemustBe"BlueBall"

product.get.detailsmustBe"AwesomeBlueBasketball"

product.get.pricemustBe19.99

}

"findall"in{

valproducts=Awaits.get(5,service.findAll())

products.get.lengthmustBe1

products.get(0).idmustBeSome(1)

products.get(0).namemustBe"BlueBall"

products.get(0).detailsmustBe"AwesomeBlueBasketball"

products.get(0).pricemustBe19.99

}

"findallproducts"in{

valproducts=service.findAllProducts()

products.lengthmustBe1

products(0)._1mustBe"1"

products(0)._2mustBe"BlueBall"

}

"remove1product"in{

valproduct=Awaits.get(5,service.remove(1))

productmustBe1

valoldProduct=Awaits.get(5,service.findById(1))

oldProductmustBeNone

}

"notremovebecausedoesnotexist"in{

intercept[RuntimeException]{

Page 285: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Awaits.get(5,service.remove(-1))

}

}

}

}

Asyoucanseeintheprecedingcode,mostfixescenteraroundthenewsignaturesandthefactweareusingFuturesandneedtousetheAwaitsutilityandmocks.Wetesttheservicewithoutthedatabasecallviathiscode:

valservice:IProductService=newProductService(newProductMockedDao)

Wecanmoveontothenextservice,whichwillbethereview.

YourReviewServiceTestSpec.scalafileshouldlooklikethis:

classReviewServiceTestSpecextendsPlaySpec{

"ReviewService"must{

valservice:IReviewService=newReviewService(new

ReviewMockedDao)

"insertareviewproperly"in{

valreview=newmodels.Review

(Some(1),Some(1),"diegopacheco","TestingisCool")

service.insert(review)

}

"updateareviewt"in{

valreview=newmodels.Review

(Some(1),Some(1),"diegopacheco","TestingsosoCool")

service.update(1,review)

}

"notupdatebecausedoesnotexist"in{

intercept[RuntimeException]{

Awaits.get(5,service.update(333,null))

}

}

"findthereview1"in{

valreview=Awaits.get(5,service.findById(1))

review.get.idmustBeSome(1)

review.get.authormustBe"diegopacheco"

review.get.commentmustBe"TestingsosoCool"

review.get.productIdmustBeSome(1)

}

"findall"in{

valreviews=Awaits.get(5,service.findAll())

reviews.get.lengthmustBe1

reviews.get(0).idmustBeSome(1)

reviews.get(0).authormustBe"diegopacheco"

reviews.get(0).commentmustBe"TestingsosoCool"

reviews.get(0).productIdmustBeSome(1)

}

"remove1review"in{

valreview=Awaits.get(5,service.remove(1))

reviewmustBe1

valoldReview=Awaits.get(5,service.findById(1))

oldReviewmustBeNone

Page 286: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

"notremovebecausedoesnotexist"in{

intercept[RuntimeException]{

Awaits.get(5,service.remove(-1))

}

}

}

}

Thatwasthereviewspecservicetestcode.Weapplythesamechangesaswedidforproduct.Nowweneedtomoveontothelastservicetest,whichwillbetheimage.

YourImageServiceTestSpec.scalafileshouldlooklikethis:

classImageServiceTestSpecextendsPlaySpec{

"ImageService"must{

valservice:IImageService=newImageService(new

ImageMockedDao)

"insertaimageproperly"in{

valimage=newmodels.Image

(Some(1),Some(1),"http://www.google.com.br/myimage")

service.insert(image)

}

"updateaimage"in{

valimage=newmodels.Image

(Some(2),Some(1),"http://www.google.com.br/myimage")

service.update(1,image)

}

"notupdatebecausedoesnotexist"in{

intercept[RuntimeException]{

Awaits.get(5,service.update(333,null))

}

}

"findtheimage"in{

valimage=Awaits.get(5,service.findById(1))

image.get.idmustBeSome(1)

image.get.productIdmustBeSome(1)

image.get.urlmustBe"http://www.google.com.br/myimage"

}

"findall"in{

valreviews=Awaits.get(5,service.findAll())

reviews.get.lengthmustBe1

reviews.get(0).idmustBeSome(1)

reviews.get(0).productIdmustBeSome(1)

reviews.get(0).urlmustBe"http://www.google.com.br/myimage"

}

"remove1image"in{

valimage=Awaits.get(5,service.remove(1))

imagemustBe1

valoldImage=Awaits.get(5,service.findById(1))

oldImagemustBeNone

}

"notremovebecausedoesnotexist"in{

intercept[RuntimeException]{

Awaits.get(5,service.remove(-1))

Page 287: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

}

}

}

Wehavefixedalltheservicestests.Nowweneedtofixthecontrollertests.

Page 288: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ControllertestsNowlet'sfixthecontrollertests.Thefirstonewillbetheproductcontroller.

YourProductControllerTestSpec.scalafileshouldlooklikethis:

classProductControllerTestSpec

extends

PlaySpec

withOneServerPerSuitewithOneBrowserPerSuitewithHtmlUnitFactory{

"ProductController"should{

DBCleaner.cleanUp()

"insertanewproductshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

}

"detailsfromtheproduct1shouldbeok"in{

goTo(s"http://localhost:${port}/product/details/1")

textField("name").valuemustBe"BlueBall"

textField("details").valuemustBe"BlueBallisaAwesome

andsimpleproduct"

textField("price").valuemustBe"17.55"

}

"updateproduct1shouldbeok"in{

goTo(s"http://localhost:${port}/product/details/1")

textField("name").value="BlueBall2"

textField("details").value="BlueBallisaAwesomeand

simpleproduct2"

textField("price").value="17.66"

submit()

goTo(s"http://localhost:${port}/product/details/1")

textField("name").valuemustBe"BlueBall2"

textField("details").valuemustBe"BlueBallisaAwesome

andsimpleproduct2"

textField("price").valuemustBe"17.66"

}

"deleteaproductshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/product")

clickonid("btnDelete")

}

Page 289: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

"Cleanupdbintheend"in{

DBCleaner.cleanUp()

}

}

}

TheControllerproducttestneedstocalltheDBCleanerfunctionatthebeginningofthetesttomakesurethatthedatabaseisinawell-knownstate;additionally,andafterrunningallthetests,weneedtocleanupthedatabasejusttobesafe.

Wewillnowapplythesamechangesforthereviewandimagecontrollertests.

YourReviewControllerTestSpecfileshouldlooklikethis:

classReviewControllerTestSpec

extendsPlaySpec

withOneServerPerSuitewithOneBrowserPerSuitewithHtmlUnitFactory{

DBCleaner.cleanUp()

"ReviewController"should{

"insertanewreviewshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/review/add")

singleSel("productId").value="1"

clickonid("author")

enter("diegopacheco")

clickonid("comment")

enter("Testsareamazing!")

submit()

}

"detailsfromthereview1shouldbeok"in{

goTo(s"http://localhost:${port}/review/details/1")

textField("author").valuemustBe"diegopacheco"

textField("comment").valuemustBe"Testsareamazing!"

}

"updatereview1shouldbeok"in{

goTo(s"http://localhost:${port}/review/details/1")

textField("author").value="diegopacheco2"

textField("comment").value="Testsareamazing2!"

submit()

goTo(s"http://localhost:${port}/review/details/1")

textField("author").valuemustBe"diegopacheco2"

textField("comment").valuemustBe"Testsareamazing2!"

}

"deleteareviewshouldbeok"in{

goTo(s"http://localhost:${port}/review/add")

singleSel("productId").value="1"

clickonid("author")

enter("diegopacheco")

Page 290: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

clickonid("comment")

enter("Testsareamazing!")

submit()

goTo(s"http://localhost:${port}/review")

clickonid("btnDelete")}

"Cleanupdbintheend"in{

DBCleaner.cleanUp()

}

}

}

Alright,wehavethetestsforthereviewcontrollerfixed.Nowwecanmovetothelastcontrollertestfortheimage.

YourImageControllerTestSpec.scalafileshouldlooklikethis:

classImageControllerTestSpec

extendsPlaySpec

withOneServerPerSuitewithOneBrowserPerSuitewithHtmlUnitFactory{

DBCleaner.cleanUp()

"ImageController"should{

"insertanewimageshouldbeok"in{

goTo(s"http://localhost:${port}/product/add")

clickonid("name")

enter("BlueBall")

clickonid("details")

enter("BlueBallisaAwesomeandsimpleproduct")

clickonid("price")

enter("17.55")

submit()

goTo(s"http://localhost:${port}/image/add")

singleSel("productId").value="1"

clickonid("url")

enter("https://thegoalisthering.files.wordpress.com/2012/01/

bluetennisball_display_image.jpg")

submit()

}

"detailsfromtheimage1shouldbeok"in{

goTo(s"http://localhost:${port}/image/details/1")

textField("url").valuemustBe

"https://thegoalisthering.files.wordpress.com/2012/01/

bluetennisball_display_image.jpg"

}

"updateimage1shouldbeok"in{

goTo(s"http://localhost:${port}/image/details/1")

textField("url").value=

"https://thegoalisthering.files.wordpress.com/2012/01/

bluetennisball_display_image2.jpg"

submit()

goTo(s"http://localhost:${port}/image/details/1")

textField("url").valuemustBe

"https://thegoalisthering.files.wordpress.com/2012/01/

bluetennisball_display_image2.jpg"

}

"deleteaimageshouldbeok"in{

Page 291: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

goTo(s"http://localhost:${port}/image/add")

singleSel("productId").value="1"

clickonid("url")

enter("https://thegoalisthering.files.wordpress.com/2012/01/

bluetennisball_display_image.jpg")

submit()

goTo(s"http://localhost:${port}/image")

clickonid("btnDelete")

}

"Cleanupdbintheend"in{

DBCleaner.cleanUp()

}

}

}

Allright,allthecontrollertestsarefixednow.WecanrunalltheteststodoublecheckwhethereverythingisOK.

Runthefollowingcommand:

$activatortest

Yougetoutputasshowninthefollowingscreenshot:

Ifyouhaveproblemsrunningtheapplication(coveredinthenextsection),applytheevolution,andthenyoucanrunthetestsagain.Testsmighttakesometime,dependingonyourhardware.

Page 292: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RunningtheapplicationNowitistimetoruntheapplicationusing$activatorrun.Openyourwebbrowser,andgotohttp://localhost:9000/.Onceyoudothat,Playwilldetectthattheapplicationneedsevolutions,andwillapplythethreeevolutionswehave(1.sql,2.sql,and3.sql).However,youwillneedtoclickonthebuttontoapplytheevolution.

Afteryouclickontheredbutton,Applythisscriptnow!,Slickwillcreatethetables,andredirectyoutotheapplication.

Page 293: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryWiththis,wereachtheendofthechapter.YoulearnedhowtoperformdatabasepersistenceusingSlick.YoualsolearnedhowtodoFRMmapping,andwerefactoredourapplicationandtestssotheyworkwithreactivepersistenceandthePlayframework.WethenexplainedhowtoaccessthedatabaseusingScalacode,andperformoperationsusingSQL.

Inthefollowingchapter,wewillseemoreaboutreports,andwewilluseourdatabasetogeneratereportsbasedonourPlayframeworkapplication.

Page 294: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter7.CreatingReportsUpuntilnow,welearnedhowtobootstrapourapplicationusingActivator,developourwebapplicationusingtheScalaandPlayframework,andaaddreactivemicroservicescallusingRxScalafordataflowcomputations.WealsoperformedunittestandcontrollertestingusingtheBDDandPlayframework.Then,wepersisteddataintoMySQLusingSlick.Nowwewillmoveonwithourapplication.

Inthischapter,youwilllearnhowtowritereportswithJasperReports.JasperReportsisaverysolidreportingsolutionforJava,anditcanbeusedinScalaveryeasily.WewillcreatedatabasereportsusingJasper,andchangeourapplicationtohavesuchfunctionality.

Inthischapter,wewillcoverthefollowingtopics:

UnderstandingJasperReportsAddingdatabasereportstoourapplication

Page 295: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntroducingJasperReportsJasperReports(http://community.jaspersoft.com/project/jasperreports-library)isaverypopularandsolidreportssolutionthatcangeneratereportsinseveralformats,suchas:

HTMLExcelWordOpenOfficeformatPDF

Inordertogetyourreports,youhaveavisualtoolcalledJaspersoftStudio,inwhichyoucandraganddropelementssuchaslabels,images,datafields,andmuchmore.Jasperwillstorethismetadata(thereportdefinition)inanXMLfile,alsoknownasJRXML.Ifyouwant,youcaneditandworkwiththisXMLwithoutanyeditor;however,itiswaybettertousetheJaspersoftStudiotooltogainproductivity.

Jaspercanworkwithseveraldatasources,suchasdatabases,XML,orevenobjectsinmemory.Forthisbook,wewillusethedatabasedatasourcetoaccessourMySQLdatabase.

Page 296: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JasperReportsworkflowJasperReportshasthesameexecutionstages,includingcompilingyourreportsandrenderinginaspecificformat,forexample,HTML.Thefirststageisthereportdesign.IfyouarenotusingtheJaspersoftStudiovisualtool,weassumethatyouhaveyourJRXMLfile.ThenextstepistocompileJRXMLintoaJasperfile.Thiscompilationphasedoesn'tneedtohappeneverytime;it'sneededonlyifyouchangetheJRXML.Otherwise,youcanusethesameJasperfile.TherearesomestrategiestocachetheJasperfile,sobasicallyyoucandoitonthebuildtimeoryoucancacheondemandintheapplication.Forourapplication,wewillbeusingthesecondapproach--cachingondemandintheapplication.

Thenextphaseistorenderorexport.YoucanexportthereporttothemanyformatsJaspersupports,suchasHTML,EXCEL,orPDF,forinstance.It'spossibletousethesamereportlayoutandexporttoasmanyformatsasyoulike.Forourapplication,wewillbeusingthePDFformat.

Page 297: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JaspersessionsAJRXMLhassectionsthatareevaluatedindifferentwaysandatdifferenttimes.Thefollowingdiagramshowsalltheavailablesessions:

Thedifferentsectionsareasfollows:

Title:ThisisprintedjustonetimePageHeader:ThisisprintedatthebeginningofallprintedpagesColumnHeader:ThisisprintedatthebeginningofeachdetailcolumnDetail:ThisiswhereeveryrecordreadfromthedatasourceisprintedColumnFooter:ThisisprintedattheendofeachdetailcolumnPageFooter:ThisisprintedattheendofallprintedpagesSummary:Thisisprintedattheendofthereport,andisoftenusedtoshowcalculations,totals,andsummarizationsingeneral

JasperisaveryflexiblereportsolutionthatalsoallowsustorungroovyscriptsinsideaJasperreporttododynamiccalculationsaswellasdynamiclayouts.Thisisusefulifyoudonotwanttoprintapagegivensomecondition,orbasedonwhatyouhaveinthedatabase,or

Page 298: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

youdonotwanttoshowsomedata.

Next,wewillinstallJaspersoftStudioandstartcreatingreportsforourapplication.

Page 299: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

InstallingJaspersoftStudio6Forthis,youwillneedtohaveJava8installed.Ifyoudon'thaveit,gobacktoChapter1,IntroductiontoFP,Reactive,andScala,andfollowthesetupinstructions.Jasperisreallygreatbecauseitworksonmultipleplatforms;however,itworksbetteronWindows.WeareusingLinux,sowewillneedtodealwithfonts.JasperReportsuseslotsofMicrosoft'scorefonts,suchasArialandTimesNewRoman.TherearesomeoptionstohavethesourcesonLinux.YoucanlookforamscorefontsinstalleronLinuxorjustcopythefontsfromWindows.

IfyouhaveadualbootLinux/Windowsinstallation,youcangotoyourWindowsdriveatthelocationWindowsDrive/Windows/Fonts.Youwillneedtocopyallfontfilesto/usr/share/fontsonLinuxandrun$sudofc-cache-fv.Thismighttakesometime--formyWindowsinstallation,itwasabout~300MBoffonts.YoucantestwhetheryouhaveWindowscorefontsonLinux.Openthewriterandcheckforthefonts.Youshouldseesomethingsimilartothis:

Whyisthissoimportant?BecauseJasperwon'tworkifyoudon'thavetherightfontinplace.Itwilljustthrowyourandomexceptionsthatwillnotmakesense,butitisverylikelytoberelatedtomissingfonts.

Page 300: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Oncewehavethefonts,wecangoaheadanddownloadJaspersoftStudio6.Forthisbook,wewillbeusingthe6.2.2version.Youcandownloaditfromhttp://community.jaspersoft.com/project/jasperreports-library/releases.IfyouareonLinux,it'shighlyrecommendedtousetheDEBpackage;otherwise,youwillneedtoinstallseveralotherdependencies.

OnceyoudownloadandinstallJaspersoftStudioandopentheprogram,youwillseeaUIsimilartothisonehere:

WehavesuccessfullyinstalledJaspersoftStudio.Now,wewillneedtoconfigureourMySQLdatasourceinordertostartcreatingreportsforourapplication.

Page 301: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringMySQLDataAdapterinJaspersoftStudioOpenJaspersoftStudio6andclickonFile|New|DataAdapterWizard.Youwillseethefollowingscreen:

FilenameshouldbeMYSQL_DATAADAPTER.xml,andthenyoucanclickNext>.

Next,wewillneedtochoosethetypeofdatabaseadapter.Thereareseveraloptions,suchasCassandra,MongoDB,HBase,JSONfile,andsoon.

Page 302: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WeneedtopickDatabaseJDBCConnectionandclickNext>.

Now,wewillneedtoconfiguretheconnectiondetails.

Page 303: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Thefieldsshouldbecompletedasfollows:

Name:MySQLJDBCDriver:com.mysql.jdbc.DriverJDBCUrl:jdbc:mysql://localhost/RWS_DB?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

Username:rootPassword:Thisneedsbeblank,orputthepasswordifyouareusingone.

WewillalsoneedtoconfigurethedriverintotheJaspersoftStudioclasspath.Aswearerunningtheapplicationinthesamebox,wealreadyhavetheMySQLdriverdownloadwithSBTonthe~/.ivy2/cache/mysql/mysql-connector-java/jars/mysql-connector-java-6.0.3.jarfolder.Wewilljustneedtopointitoutonthethirdtab,calledDriverClasspath.

Page 304: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Nowwecantesttheconnectiontoseeifit'sallgood.

Page 305: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Great!NowwehaveourMySQLDatabaseAdapterconfigured,andwearereadytostartcreatingreportsforyourapplication.

Page 306: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingaproductreportTocreateaproductreport,clickonFile|New|JasperReport.ThenselecttheInvoicetemplate.

NowyoucanclickonNext>andwewillsetupthenameofthereport.ThefilenamewillbeProducts.jrxml.ClickonNext>.Then,wewillneedtoselecttheDataSource:MySQL.

Page 307: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Now,youwillneedtoruntheSelectname,details,pricefromProduct;query.

AftersettingtheSQLquery,youcanclickonNext>.

Page 308: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Next,youwillneedtopickthefieldsthatwillbeusedinthereport.Selectallthefieldsfromtheleftlistandmovethemtotherightlist.ThenclickonNext>.Wedon'tneedgrouporderingforthisreport,sojustskipthegroupandclickonNext>again.

Congratulations!Wefinishedthesetup.Wecantakealook(haveareportpreview)ofthereportusingJaspersoftStudio.JustclickonthenewreportcalledProducts.jxml.Wewillremoveallthefieldsthatwedon'tneed,aswellasthelogo.Thenthereportwilllooklikethis:

Page 309: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WewillchangethetitletoProductsanddropallotherinformationbuttheNAME,DETAILS,andPRICEheaders,whichwillberetained.Wewillalsokeepthe$F{NAME},$F{DETAILS},and$F{PRICE}fields,whichwillcomefromtheMySQLdatabase.

Page 310: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Now,wecanseethereportpreview.WewillneedtoclickonthebottomtabnamedPreview.Thereareseveralpreviewoptions.WewillhavetopickMySQLasadatasourcefromthetopofthescreenandtheexportedformat;here,weareusingJavatoseetheUI.Youcanalsopickotherformats,suchasPDF,forinstance.

Next,wewillneedtocreatereportsforreviewsandimages.

Page 311: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingareviewreportNow,wewillcreatethereviewreport.Wewilluseaverysimilarprocesstotheonethatweusedfortheproductreport.Let'sgetstartedoncreatingareviewreport:

1. ClickonFile|New|JasperReport.SelecttheInvoicetemplateandclickonNext>.2. ThefilenamewillbeReviews.jrxml.ThenclickonNext>.3. ChooseMySQLfromDataAdapterandclickonNext>.4. Query(Text)shouldcontainthefollowingcodesnippet:

Selectp.name,r.author,r.comment

fromProductp,Reviewr

wherep.id=r.product_id;

5. ThenclickNext>.6. Selectallthefields:name,author,andcommentthenclickNext.>7. Let'sskipthegroupbysectionandclickonNextandthenFinish.8. Wewillremovealltemplatelabelsandfieldsandjustkeepthedatabasefields,sowe

shouldhavesomethinglikethis:

Page 312: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

That'sit!Wehavethereviewreport.Ifyoulike,youcanclickonthePreviewtabatthebottomofthescreenandselectMySQLandJavatoseethereport.Keepinmindthatyouwillneedtohavedata;otherwise,itwillbeempty.

Page 313: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatinganimagereportNowwewillcreatetheimagereport.Wewillfollowaverysimilarprocesstotheoneweusedfortheproductandreviewreport.AswehaveanimageURL,wewillalsodisplaytheimage,sowewillneedtouseadifferentcomponent.Let'sgetstartedoncreatinganimagereport:

1. ClickonFile|New|JasperReport.2. SelecttheInvoicetemplateandclickonNext.3. ThefilenamewillbeImages.jrxml.ThenclickonNext.4. ChooseMySQLfromDataAdapterandclickonNext>.5. Query(Text)shouldcontainthefollowingcodesnippet:

Selectp.name,i.url

fromImagei,Productp

wherep.id=i.product_id;

6. ThenclickNext>.7. Selectallthefields:name,url,andthenclickNext>.8. Let'sskipthegroupbysectionandclickonNext>andthenFinish.

Nowwewillneedtoremovealllabelsandfields,aswedidfortheotherreports,andjustkeepthelabelsandfieldsfromthedataadapter.

WeneedtoaddanimagecomponentcalledImage.Youcanfinditinthepaletteattheright-handsidecalledBasicElements.Justdraganddropitintothedetailband,asshowninthefollowingscreenshot:

Page 314: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Selectacustomexpressionandthentype$F{url}.

That'sit!Nowthatwehavetheimagereportwithimages,it'stimetochangethePlayframeworkapplicationinordertorenderthisreportinPDFformatoverthere.

Page 315: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntegratingJasperReportswithPlayframeworkWewillneedtocreateanewfolderunderReactiveWebStore/appcalledreports.Then,wewillcopyallthreenew.jrxmlfilesfromtheJaspersoftStudiotothisfolderandsetupthebuilddependencies.

Page 316: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

build.sbtFirstofall,wewillneedtoaddnewdependenciestothebuild.sbtfile.

Yourbuild.sbtfileshouldlooklikethisafteraddingtheJasperdependencies:

libraryDependencies++=Seq(

//....Otherdependencies....

"net.sf.jasperreports"%"jasperreports"%"6.2.2"withSources()

,"net.sf.jasperreports"%"jasperreports-functions"%"6.2.2",

"net.sf.jasperreports"%"jasperreports-chart-themes"%"6.2.2"

)

resolvers+="Jasper"at

"https://jaspersoft.artifactoryonline.com/jaspersoft/repo/"

resolvers+="JasperSoft"at

"https://jaspersoft.artifactoryonline.com/jaspersoft/jaspersoft-

repo/"

resolvers+="Jasper3rd"at

"https://jaspersoft.artifactoryonline.com/jaspersoft/

jaspersoft-3rd-party/"

resolvers+="mondrian-repo-cache"at

"https://jaspersoft.artifactoryonline.com/jaspersoft/

mondrian-repo-cache/"

resolvers+="spring-mil"at"http://repo.spring.io/libs-milestone"

resolvers+="spring-rel"at"http://repo.spring.io/libs-release"

resolvers+="oss"at

"https://oss.sonatype.org/content/groups/public/"

So,basically,weaddedallJasperReportsdependenciesandresolvers,whichareabunchofremoterepositorieswhereSBTcanlookforthejarfiles.Youcanrunthe$activatorcompilecommandontheconsoleinordertoreloadthenewdependencies.Afterrunningcompile,itisimportanttogenerateeclipsefilesagain,soyouwillneedtorun$activatoreclipse.

Page 317: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

GenericreportbuilderNowisthetimetocodeinScala.WewillcreateagenericreportbuilderinScala.UnderReactiveWebStore/app/reports,wewillcreateanewScalaclasscalledReportBuilder.scala.

YourReportBuilder.scalafileshouldhavethefollowingcode:

packagereports

objectReportBuilder{

privatevarreportCache:scala.collection.Map[String,Boolean]=

newscala.collection.mutable.HashMap[String,Boolean].empty

defgenerateCompileFileName(jrxml:String):String=

"/tmp/report_"+jrxml+"_.jasper"

defcompile(jrxml:String){

if(reportCache.get(jrxml).getOrElse(true)){

JasperCompileManager.compileReportToFile(new

File(".").getCanonicalFile+"/app/reports/"+jrxml,

generateCompileFileName(jrxml))

reportCache+=(jrxml->false)

}

}

deftoPdf(jrxml:String):ByteArrayInputStream={

try{

valos:OutputStream=newByteArrayOutputStream()

valreportParams:java.util.Map[String,Object]=new

java.util.HashMap()

valcon:Connection=DriverManager.getConnection

("jdbc:mysql://localhost/RWS_DB?user=root&password

=&useUnicode=true&useJDBCCompliantTimezoneShift

=true&useLegacyDatetimeCode=false&serverTimezone=UTC")

compile(jrxml)

valjrprint:JasperPrint=JasperFillManager.fillReport

(generateCompileFileName(jrxml),reportParams,con)

valexporter:JRPdfExporter=newJRPdfExporter()

exporter.setExporterInput(newSimpleExporterInput(jrprint))

exporter.setExporterOutput

(newSimpleOutputStreamExporterOutput(os));

exporter.exportReport()

newByteArrayInputStream

((os.asInstanceOf[ByteArrayOutputStream]).toByteArray())

}catch{

casee:Exception=>thrownewRuntimeException(e)

}

}

}

Firstofall,wearesettingatemporarydirectorytostoretheJaspercompiledfilesinthegenerateCompileFileNamefunction.Asyoucansee,wearestoringthecompiledreportsat/tmp/.Ifyoudon'tuseLinux,youwillneedtochangethispath.

Next,wehavethecompilefunction,whichreceivesaJRXMLreportinparameter.ThereisareportcacheMapobjecttoperformanon-demandcachefortheJasperfiles.Thismaphasthe

Page 318: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JRXMLreport,isthekeyasaBooleanfile.Thissolutionallowsyoutocompilereportsondemand.

Finally,wehavethetoPdffunctionthatwillreceivethejrxmlfunctionandcompilethereportthatisneeded.ThisfunctionusesDriverManagertogettheSQLconnectioninordertosendtheconnectiontotheJasperengine.Finally,thereisthefill,processmanagedbyJasperFillManager,whichwillreceivetheJasperfileandreportsparameters(forus,anemptymap)andtheSQLconnection.

Afterfillingthereportwithdatafromdatabase,wecanexportthereportinPDFusingtheJRPdfExportercommand.Asthisisagenericfunction,wewillreturnaByteArrayInputStream,whichisanin-memorystreamstructure.

Now,thenextstepistochangeourcontrollersinordertobeabletogeneratereportsforproducts,reviews,andimages.

Page 319: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingthereporttotheproductcontrollerWewillneedtochangetheproductcontrollerinordertoexposethenewreportfunction.

YourProductController.scalafile,afteraddingthereportfunction,shouldlooksomethinglikethis:

@Singleton

classProductController@Inject()(valmessagesApi:MessagesApi,val

service:IProductService)extendsControllerwithI18nSupport{

//...restofthecontrollercode...

defreport()=Action{

importplay.api.libs.concurrent.

Execution.Implicits.defaultContext

Ok.chunked(Enumerator.fromStream(

ReportBuilder.toPdf("Products.jrxml")))

.withHeaders(CONTENT_TYPE->"application/octet-stream")

.withHeaders(CONTENT_DISPOSITION->"attachment;

filename=report-products.pdf"

)

}

}

Righthere,wehaveanewfunctioncalledreport.WewillneedtouseourReportBuildermethodpassingtheProducts.jrxmlasparameter.WeareusingtheOk.chunkedfunctioninordertobeabletostreamthereporttothebrowser.Wearealsosettingsomeresponseheaders,suchasthecontenttypeandthenameofthefile,whichwillbereportedtoproducts.pdf.

Now,wewillapplythesamecodetothereviewandimagecontrollers.

Page 320: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingthereporttothereviewcontrollerNowisthetimetocreatethereportfunctionforthereviewcontroller.Herewego.

YourReviewController.scalafile,afteraddingareportfunction,shouldlooksomethinglikethis:

@Singleton

classReviewController@Inject()

(valmessagesApi:MessagesApi,

valproductService:IProductService,

valservice:IReviewService)

extendsControllerwithI18nSupport{

//...restofthecontrollercode...

defreport()=Action{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

Ok.chunked(Enumerator.fromStream(

ReportBuilder.toPdf("Reviews.jrxml")))

.withHeaders(CONTENT_TYPE->"application/octet-stream")

.withHeaders(CONTENT_DISPOSITION->"attachment;

filename=report-reviews.pdf")

}

}

Wehavethesamelogichereaswehavefortheproductcontroller.Themaindifferenceisthejrxmlfileandthefilenameresponseheader.Now,wecanmovetothelastcontroller--theimagecontroller.

Page 321: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingthereporttotheimagecontrollerFinally,wewillapplythesamelogichereaswedidfortheproductandreviewcontroller,butnowitistimetochangetheimagecontroller.

YourImageController.scalafile,afteraddingreportfunction,shouldlooksomethinglikethis:

@Singleton

classImageController@Inject()

(valmessagesApi:MessagesApi,

valproductService:IProductService,

valservice:IImageService)

extendsControllerwithI18nSupport{

//...restofthecontrollercode...

defreport()=Action{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

Ok.chunked(Enumerator.fromStream(

ReportBuilder.toPdf("Images.jrxml")))

.withHeaders(CONTENT_TYPE->"application/octet-stream")

.withHeaders(CONTENT_DISPOSITION->"attachment;

filename=report-images.pdf")

}

}

Alright,wehavefinishedallthecontrollers.However,wewillneedtoconfigureroutes,otherwise,wewon'tbeabletocallthecontrollers--thisisthenextstep.

Page 322: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Routes-addingnewreportroutesNow,wewillneedtoaddthenewroutesforthereports.Forthat,wewilledittheconf/routesfile,asfollows:

GET/reportscontrollers.HomeController.reports

#

#Reports

#

GET/product/reportcontrollers.ProductController.report

GET/review/reportcontrollers.ReviewController.report

GET/image/reportcontrollers.ImageController.report

Wearedonewithroutesnow,andweneedtochangetheUIinordertoexposethenewreportfunctionality.Wewillcreateanewviewcontainingallreports,and,forthesakeofease,wewillalsoaddabuttonforeachresourceUI(product,review,andimage).

Page 323: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

NewcentralizedreportsUIWewillneedtocreateanewviewatReactiveWebStore/views/reports_index.scala.html.

Yourreports_index.scala.htmlfileshouldlooksomethinglikethis:

@()(implicitflash:Flash)

@main("Reports"){

<ahref="/product/report"><imgheight="42"width="42"

src="@routes.Assets.at("images/product.png")">Products

Report</a><BR>

<ahref="/review/report"><imgheight="42"width="42"

src="@routes.Assets.at("images/review.png")">ReviewsReport

</a><BR>

<ahref="/image/report"><imgheight="42"width="42"

src="@routes.Assets.at("images/image.png")">Images

Report</a><BR>

}

Sohere,wewillbasicallylistallresources--product,review,andimagesandlinktherelativecontrollers,andwhentheuserclicksontherespectivelinkaPDFreportwillbedownloaded.Nowweneedtoediteachresource(product,image,andreview)viewinordertoaddalinkforthereportsthereaswell.

Page 324: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingthereportbuttonforeachviewLet'sedittheproductviewfirst.

Yourproduct_index.scala.htmlfileshouldlooksomethinglikethis:

@(products:Seq[Product])(implicitflash:Flash)

@main("Products"){

//...restoftheuicode...

<p>

<ahref="@routes.ProductController.blank"class="btnbtn-

success">

<iclass="icon-plusicon-white"></i>AddProduct</a>

<ahref="@routes.ProductController.report"class="btnbtn-

success">

<iclass="icon-plusicon-white"></i>ProductsReport</a>

</p>

}

Asyoucanseehere,weaddedanewbuttonpointingtothenewreportfunction.WewillneedtodothesameforthereviewandtheimageUI.

Yourreview_index.scala.htmlfileshouldlooksomethinglikethis:

@(reviews:Seq[Review])(implicitflash:Flash)

@main("Reviews"){

//...restoftheuicode...

<p>

<ahref="@routes.ReviewController.blank"class="btnbtn-

success"><iclass="icon-plusicon-white"></i>AddReview</a>

<ahref="@routes.ReviewController.report"class="btnbtn-

success"><iclass="icon-plusicon-white"></i>ReviewReport</a>

</p>

}

Nowwecanaddthefinalbuttontotheimageview.

Yourimage_index.scala.htmlfileshouldlooksomethinglikethis:

@(images:Seq[Image])(implicitflash:Flash)

@main("Images"){

//...restoftheuitemplate...

<p>

<ahref="@routes.ImageController.blank"class=

"btnbtn-success"><iclass="icon-plusicon-white"></i>Add

Image</a>

<ahref="@routes.ImageController.report"class=

"btnbtn-success"><iclass="icon-plusicon-white"></i>

ImagesReport</a>

</p>

}

Allset!Nowwecanrun$activatorrunandseethenewUIandreportbuttons.Goto

Page 325: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

http://localhost:9000/:

Ifyougotohttp://localhost:9000/reports,orclickonReports,youwillseethefollowing:

Page 326: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

That'sit!WehaveallthereportsworkingonthePlayframeworkapplication.

Page 327: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtocreatecustomreportsusingJaspersoftStudioandJasperReports.Additionally,youalsochangedyourapplicationinordertointegratethePlayframeworkandJasperReports.

Inthenextchapter,youwilllearnhowtousetheAkkaframework.Wewillcontinuebuildingourapplicationandembracetheactormodelforanewkillerfeatureforyourapplication.

Page 328: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter8.DevelopingaChatwithAkkaInthepreviouschapters,wepersisteddataintoMySQLusingSlickandwrotePDFreportsusingJasperreports.NowwewilladdmorefeaturesinourappusingAkka.

Inthischapter,youwilllearnhowtocreateActorsusingtheAkkaframework.WewilluseActorsincombinationwiththePlayframeworkandWebSocketsinordertohaveachatcapability.

Wewillcoverthefollowingtopicsinthischapter:

UnderstandingtheActormodelActorsystems,Actorrouting,anddispatchersMailboxes,Actorconfiguration,andpersistenceCreatingourChatApplicationTestingourActors

Page 329: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingthenewUIintroductiontoAkkaAkka(http://akka.io/)isaframeworktobuildconcurrent,distributed,andresilientmessage-drivenapplicationsinScala,Java,and.NET.BuildingapplicationswithAkkahasseveraladvantages,whichareasfollows:

Highperformance:Akkadeliversupto50millionmessagespersecondonacommodityhardwarehaving~2.5millionActorsperGBofRAM.Resilientbydesign:Akkasystemshaveself-healingpropertiesforlocalandremoteActors.Distributedandelastic:Akkahasallthemechanismstoscaleyourapplication,suchascluster,loadbalancing,partitioning,andsharding.AkkaletsyougroworshrinkyourActorsondemand.

TheAkkaframeworkprovidesgoodabstractionsforconcurrent,asynchronous,anddistributedprogramming,suchasActors,Streams,andFutures.Thereareplentyofgreatsuccesscasesinproduction,suchasBBC,Amazon,eBay,Cisco,TheGuardian,Blizzard,Gilt,HP,HSBC,Netflix,andsomanyothers.

Akkaisatrulyreactiveframeworkbecauseeverything,inthesenseofsendingandreceivingmessagestoActors,islockless,non-blockingIO,andasynchronous.

Page 330: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntroductiontotheActormodelThekeyforconcurrencyprogrammingistoavoidasharedmutablestate.Asharedstateoftenrequireslocksandsynchronization,whichmakesyourcodelessconcurrentandmorecomplex.Actorssharenothing;theyhaveinternalstate,buttheydon'tsharetheirinternalstate.

Actorshavelocationtransparency;theycanruninalocalorremotesystemandacluster.It'salsopossibletomixlocalandremoteactors-thisisgreatforscalabilityandfitsperfectlyintoacloudenvironment.Actorscanrunanywhere,fromyourlocalbox,thecloud,bare-metaldatacenter,andLinuxcontainers.

Page 331: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WhatisanActor?Actorscanbealternativestothreads,callbacklisteners,singletonservices,EnterpriseJavaBeans(EJB),routers,loadbalancerorpool,andafinite-statemachine(FSM).TheActormodelconceptisnotnewatall;itwascreatedbyCarlHewittin1973.TheActormodelisheavilyusedinthetelecomindustryinrock-solidtechnologiessuchasErlang.ErlangandtheActormodelhadimmensesuccesswithcompaniessuchasEricssonandFacebook.

Actorshaveasimplewayofworking:

Unitofcodeorganization:ProcessingStorageCommunication

TheymanagetheinternalstatesTheyhaveamailboxTheycommunicatewithotheractorsusingmessagesTheycanchangethebehavioratruntime

Page 332: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

MessageexchangeandmailboxesActorstalkwitheachotherviamessaging.Therearetwopatterns:oneiscalledaskandtheotheriscalledfireandforget.Bothmethodsareasynchronousandnon-blockingIO.WhenanActorsendsamessagetoanotherActor,itdoesnotsendthemessagedirectlytotheotherActor;itactuallysendsittotheActor'smailbox.

MessagesareenqueuedintheActormailboxinatime-orderedfashion.TherearedifferentmailboxesimplementationsinAkka.ThedefaultisFirstInFirstOut(FIFO)based.Thisisagooddefault;however,youmightneedadifferentalgorithm,whichisfineasyoucanchangethemailboxifyouneedto.Moredetailscanbefoundintheofficialdocumentation(http://doc.akka.io/docs/akka/2.4.9/scala/mailboxes.html#mailboxes-scala).ActorsliveinanActorsystem.YoucanhavemultipleActorsystemsinacluster:

AkkaencapsulatestheactorstateinmailboxanddecouplesitfromtheActorbehavior.TheActorbehavioristhecodeyouwillhaveinsideyourActor.YouwillneedtoseeActorsandAkkaasaprotocol.So,basically,youwillneedtodefinehowmanyActorsyouwillhaveandwhateachActorwilldointhesenseofcode,responsibility,andbehavior.TheActorsystem

Page 333: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

hasActorsandsupervisors.SupervisorsareoneoftheAkkamechanismstodeliverfaulttoleranceandresiliency.SupervisorstakecareoftheActorinstances,andtheycanrestart,kill,orcreatemoreActorsasneeded.

TheActormodelisgreatforconcurrencyandscalability;however,likeeverysinglethingincomputerscience,therearetradeoffsandcons.Forinstance,Actorsrequireanewmindsetandadifferentwayofthinking.

Thereisnosilverbullet.Onceyouhaveyourprotocol,itmightbehardtoreuseyourActorsoutsideyourprotocol.Ingeneral,Actorscanbehardertocompose,ascomparedtoobject-orientedclassesorfunctionsinFunctionalProgramming,forinstance.

Page 334: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CodingactorswithAkkaLet'stakealookatthefollowingActorcodeusingtheAkkaframeworkandScala:

importakka.actor._

caseobjectHelloMessage

classHelloWorldActorextendsActor{

defreceive={

caseHelloMessage=>sender()!"HelloWorld"

casea:Any=>sender()!"Idon'tknow:"+a+"-Sorry!"

}

}

objectSimpleActorMainAppextendsApp{

valsystem=ActorSystem("SimpleActorSystem")

valactor=system.actorOf(Props[HelloWorldActor])

importscala.concurrent.duration._

importakka.util.Timeout

importakka.pattern.ask

importscala.concurrent.Await

implicitvaltimeout=Timeout(20seconds)

valfuture=actor?HelloMessage

valresult=Await.result(future,

timeout.duration).asInstanceOf[String]

println("Actorsays:"+result)

valfuture2=actor?"Cobol"

valresult2=Await.result(future2,

timeout.duration).asInstanceOf[String]

println("Actorsays:"+result2)

system.terminate()

}

IfyourunthisAkkacodeonsbtinyourconsole,youwillseeanoutputsimilartothis:

$sbtrun

Page 335: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Let'stakeacloserlookatthisAkkacodewejustwrote,inwhichwedefinedaScalaclasscalledHelloWorldActor.InorderforthisclassbeanActor,wewillneedtoextendActor.Actorsarereactivebydefault,whichmeansthattheyarewaitingtoreceivemessagestoreacttothemessages.Youwillneedtocodeyourbehaviorinaneventloop.InAkka,thisisdonebycodingthereceivefunctionwithapatternmatcherinScala.

Thepatternmatcherwilldefinewhattheactorcando.Youwillneedtocodeallthepossiblekindsofmessagesyouwantthatactortohandle.AsImentionedearlier,youwillneedtohaveaprotocol;soyourprotocolhasamessagecalledHelloMessage.It'sacommonpracticeinAkkatouseScalaobjectsasmessages.However,youcanpassprettymuchalltypesasmessages.It'sevenpossibletosendcaseclasseswithparameters.

Alright,wehaveourprotocol,whichisourActors,andthemessagestheycanexchange.NowwewillneedtocreateanActorsystemandstartourapplication.Asyoucansee,wewillusetheActorSystemobjecttocreateanActorsystem.Actorsystemsneedtohaveaname,whichcanbeanystringyoulike,aslongasitcontainsanyletter[a-z,A-Z,0-9]andnon-leading'-'or'_'.

Aftercreatingthesystem,youcancreateActors.ThesystemhasafunctioncalledactorOf,whichcanbeusedtocreateActors.YouwillneedtouseaspecialobjectcalledPropsandpasstheactorclass.Whydoweneeditthisway?It'sbecauseAkkamanagestheActorstate.YoushouldnottrytomanagetheActorinstancebyyourself.Thisisdangerousbecauseyoucanbreakreferentialtransparencyandyourcodemightnotwork.

Forthiscode,weareusingtheaskpattern.WewillusethistosendmessagestotheActor,andwewanttoknowwhattheActorwillreturn.Akkadoeseverythinginanasyncandnon-blockingway,asmentionedpreviously.However,sometimesyouwanttogettheanswernowandthen,unfortunately,youwillneedtoblock.

Inordertogettheanswernow,wewillneedtodefineatimeoutandusetheAwaitobject.WhenyousendamessagetoanActorusing?(theaskpattern),AkkawillreturnaFutureforyou.Then,youcanpasstheFuturewithatimeouttoAwait,andiftheanswercomesbackbeforethetimeout,youwillhavetheresponsefromtheActor.

Again,weareblockingherebecausewewanttogettheanswernow,andweareoutsidetheActorsystem.KeepinmindthatwhenanActortalkswithanotherActorinsidetheActorsystem,itshouldnotblockever.SobecarefulwiththeusageofAwait.

Anotherimportantthinginthiscodeisthatthesender()methodinsideoftheActorreceivesafunction.ThismeansthatyouwanttogetthereferenceoftheActorwhosendsthemessagetoyou.Asweareperformingsender()!method,wearesendingananswerbacktothecaller.Thesender()functionisanAkkaabstractiontodealwithresponsemessagestootherActorsorfunctioncallers.

Wealsohaveanothercase,withAny,whichmeansallothermessageswillbehandledbythat

Page 336: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

casecode.

TheaskpatternisonewaytosendmessagestoActors.ThereisanotherpatterncalledFireAndForget"!".Fireandforgetwillsendamessageandwillnotblockandwaitfortheanswer.So,thereisnoanswer-inotherwords,Unit.

Let'slookatsomecodewiththeFireAndForgetmessageexchange:

importakka.actor._

objectMessage

classPrinterActorextendsActor{

defreceive={

casea:Any=>

println("Print:"+a)

}

}

objectFireAndForgetActorMainAppextendsApp{

valsystem=ActorSystem("SimpleActorSystem")

valactor=system.actorOf(Props[PrinterActor])

valvoidReturn=actor!Message

println("Actorsays:"+voidReturn)

system.terminate()

}

Ifyourunthiscodewith$sbtrun,youwillseeanoutputasfollows:

Here,wehaveaPrinterActormethod,whichacceptsprettymuchanythingandprintsontheconsole.Then,wewillcreateanActorsystemandjustsendamessagetoourActorwiththefireandforgetpattern,a.k.a"!",andasyoucansee,wewillreceiveUnit;finally,wewillawaittheshutdownoftheActorsystemusingtheterminateoption.

Page 337: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ActorroutingAkkaprovidesroutingfunctionality.ThisisusefulfromabusinesspointofviewbecauseyoucanroutetotherightActorinthesenseofbusinesslogicandbehavior.Forarchitecture,wecanusethisasloadbalancingandroutemessagestomoreActorstoachievefaulttoleranceandscalability.

Akkahasseveraloptionsforrouting,whichareasfollows:

RoundRobin:ThisisarandomlogictoeverydifferentActoronthepool.SmallestMailbox:ThissendsthemessagetotheActorwithfewermessages.ConsistentHashing:ThispartitionstheActorsperhashID.ScatterGather:Thissendsmessagetoallactors,andthefirsttoreplywins.TailChopping:Thissendstoarouterandomly,andifareplydoesn'tcomebackinasecond,itchoosesanewrouteandsendsagain,andsoon.

Let'sseethefollowingcodeinpractice:

importakka.actor._

importakka.routing.RoundRobinPool

classActorUpperCasePrinterextendsActor{

defreceive={

cases:Any=>

println("Msg:"+s.toString().toUpperCase()+"-"+

self.path)

}

}

objectRoutingActorAppextendsApp{

valsystem=ActorSystem("SimpleActorSystem")

valactor:ActorRef=system.actorOf(

RoundRobinPool(5).props(Props[ActorUpperCasePrinter]),name=

"actor")

try{

actor!"works1"

actor!"works2"

actor!"works3"

actor!"works4"

actor!"works5"

actor!"works6"

}catch{

casee:RuntimeException=>println(e.getMessage())

}

system.terminate()

}

Ifyourunthiscodeinsbtdoing$sbtrun,youwillgetanoutputasfollows:

Page 338: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

So,herewehaveanActorUppercasePrinterfunctionthatprintswhateveritreceivesandcallsthetoStringfunction,andthentoUpperCase.Finally,italsoprintstheself.pathActor,whichwillbetheaddressoftheActor.Actorsarestructuredinahierarchicalstructure,similartoafilesystem.

TherearemultiplewaystouseAkka-Akkasupportscodeorconfiguration(application.conffile).Here,wearecreatingaround-robinpoolactorthathasfiveroutes.WearepassingthetargetActortotherouterthatwillbeourprinterActor.

Asyoucansee,whenwesendmessagesusingthefireandforgetpattern,everymessageisdeliveredtoadifferentActor.

Page 339: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PersistenceAkkaworksonmemory.However,itispossibletousepersistence.PersistenceisstillkindofexperimentalinAkka.However,itisstable.Forproduction,youcanuseadvancedpersistenceplugins,suchasApacheCassandra.Forthesakeofdevelopmentandeducation,wewilluseGoogleleveldbinourfilesystem.Akkahasmultiplepersistenceoptions,suchasviewsandpersistentActors.

Let'stakealookatapersistentactorusingtheGoogleleveldbandfilesystem:

importakka.actor._

importakka.persistence._

importscala.concurrent.duration._

classPersistenceActorextendsPersistentActor{

overridedefpersistenceId="sample-id-1"

varstate:String="myState"

varcount=0

defreceiveCommand:Receive={

casepayload:String=>

println(s"PersistenceActorreceived${payload}(nr=

${count})")

persist(payload+count){evt=>

count+=1

}

}

defreceiveRecover:Receive={

case_:String=>

println("recover...")

count+=1

}

}

objectPersistentViewsAppextendsApp{

valsystem=ActorSystem("SimpleActorSystem")

valpersistentActor=

system.actorOf(Props(classOf[PersistenceActor]))

importsystem.dispatcher

system.scheduler.schedule(Duration.Zero,2.seconds,

persistentActor,"scheduled")

}

Executingthe$sbtruncommandwillgiveyouthefollowingoutput:

Page 340: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Ifyourunthiscodewith$sbtrun,andstopandrunagain,youwillseethedataisbeingstoredandrecoveredeverytimeyoustopandstartagain.

Asyoucansee,yourActorneedstoextendPersistentActorinordertohavepersistencesupport.YouwillalsoneedtoprovideapersistenceID.

Here,youwillneedtoimplementtworeceivefunctions.Oneisforcommands(alsoknownasmessages),andtheotheroneisforrecovery.Thecommand'sreceiveloopwillbeactivatedwhenthisActorreceivesmessages,whiletherecoveronewillbeactivatedwhentheActorbootsupandwillreadthepersistentdatafromthedatabase.

So,thisActorherehasacountertocounteachmessageitreceives,andprintseverymessageitgetsontheconsole.That'sit;asyoucansee,itisprettysimple.Inordertousethisfunctionality,youwillalsoneedtoconfigureyourapplication.conf.

Yourapplication.conffileshouldlooksomethinglikethis:

akka{

system="SimpleActorSystem"

remote{

log-remote-lifecycle-events=off

netty.tcp{

hostname="127.0.0.1"

port=0

Page 341: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

}

}

akka.cluster.metrics.enabled=off

akka.persistence.journal.plugin=

"akka.persistence.journal.leveldb"

akka.persistence.snapshot-store.plugin=

"akka.persistence.snapshot-store.local"

akka.persistence.journal.leveldb.dir="target/persistence/journal"

akka.persistence.snapshot-store.local.dir=

"target/persistence/snapshots"

#DONOTUSETHISINPRODUCTION!!!

#Seealsohttps://github.com/typesafehub/activator/issues/287

akka.persistence.journal.leveldb.native=false

So,herewearedefiningasimpleAkkasystem(localmode),andweareconfiguringthepersistenceforGoogleleveldb.Asyoucansee,wewillneedtoprovideapathforpersistence,andthispathmustexistontheOS.

Asweareusinganadditionalfunctionality,wewillalsoneedtochangebuild.sbtinordertoimportalljarsthatwewillneedinthesenseofAkka,persistence,andleveldb.

Yourbuild.sbtfileshouldlooksomethinglikethis:

//restofthebuild.sbtfile...

valakkaVersion="2.4.9"

libraryDependencies+="com.typesafe.akka"%%"akka-actor"%

akkaVersion

libraryDependencies+="com.typesafe.akka"%%"akka-kernel"%

akkaVersion

libraryDependencies+="com.typesafe.akka"%%"akka-remote"%

akkaVersion

libraryDependencies+="com.typesafe.akka"%%"akka-cluster"%

akkaVersion

libraryDependencies+="com.typesafe.akka"%%"akka-contrib"%

akkaVersion

libraryDependencies+="com.typesafe.akka"%%"akka-persistence"%

akkaVersion

libraryDependencies+="org.iq80.leveldb"%"leveldb"%"0.7"

libraryDependencies+="org.iq80.leveldb"%"leveldb-api"%"0.7"

libraryDependencies+="org.fusesource.leveldbjni"%"leveldbjni"%

"1.8"

libraryDependencies+="org.fusesource.leveldbjni"%"leveldbjni-

linux64"%"1.8"

libraryDependencies+="org.fusesource"%"sigar"%"1.6.4"

libraryDependencies+="org.scalatest"%"scalatest_2.11"%"2.2.6"

That'sit.That'sallweneedtopersisttheActor'sstate.

Note

Akkahaswaymorefunctionalities.Formore,checkoutthedefaultdocumentationathttp://doc.akka.io/docs/akka/2.4/scala.html?_ga=1.12480951.247092618.1472108365.

Page 342: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingourchatapplicationNowthatweknowAkkabetter,wewillcontinuetodevelopourapplication.AkkahasagreatintegrationwiththePlayframework.WewilluseActorswiththeAkkaandPlayframeworkrightnow.Let'sbuildasimplechatfeatureforourapp.WewillchangethecodetoaddanewUIand,usingtheAkkatestkit,wewilltestoutactors.

ThePlayframeworkalreadyincludesAkkaontheclasspathforus,sowedon'tneedtoworryaboutit.However,wewillneedtoaddtheAkkatestkitdependencytothebuild.sbtfileinordertohavetheclassesinourclasspath.

Yourbuild.sbtshouldlooksomethinglikethis:

//restofthebuild.stb...

libraryDependencies++=Seq(

"com.typesafe.akka"%%"akka-testkit"%"2.4.4"%Test,

//restofthedeps...

)

//restofthebuild.stb...

Okay,nowyoucangototheconsoleandtype$activator,$reload,andthen$compile.Thiswillforcesbttodownloadthenewdependency.

NowwewillneedtocreateapackagecalledActors.ThispackageneedstobelocatedatReactiveWebStore/app/.WewillstartcreatinganActorHelperutilityobjectinordertohaveagenericfunctionfortheaskpatternthatwesawearlier.ItisanActorhelpergenericaskpatternutility.

YourActorHelper.scalafileshouldlooksomethinglikethis:

packageactors

objectActorHelper{

importplay.api.libs.concurrent.

Execution.Implicits.defaultContext

importscala.concurrent.duration._

importakka.pattern.ask

importakka.actor.ActorRef

importakka.util.Timeout

importscala.concurrent.Future

importscala.concurrent.Await

defget(msg:Any,actor:ActorRef):String={

implicitvaltimeout=Timeout(5seconds)

valresult=(actor?msg).mapTo[String].map{result=>

result.toString}

Await.result(result,5.seconds)

}

}

TheActorHelperhasjustonefunction:get.ThisfunctionwillgetananswerfromanyActorgiveninanymessage.However,asyoucansee,wehaveatimeoutoffiveseconds.Ifthe

Page 343: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

resultdoesnotcomebackinthistime,anexceptionwillberaised.

Inthiscode,wearealsomappingtheActorresulttoaStringcallingthetoStringfunctionintheresultfuture.Thisisnotalotofcode;however,therearelotsofimports,anditmakesthecodecleanerandwecangetanswersfromActorswithlesscodeandfewerimports.

Page 344: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThechatprotocolNowwewillneedtodefineourprotocol.Forthisfunctionality,wewillneedthreeActors.TheActorsthatwecreatewillbeasfollows:

ChatRoom:ThiswillhaveareferenceforallusersinthechatroomChatUser:Thiswillhaveoneinstanceperuser(activebrowser)ChatBotAdmin:ThissimpleBotAdminwillprovidestatsaboutthechatroom

ChatUserActorwillneedtojoinJoinChatRoomobjectinordertostartchatting.ChatUserActorwillalsoneedtosendmessagestoChatMessageclasstotheChatRoomActorthatwillbroadcastmessagestoallusers.TheChatBotAdminwillgetareportfromGetStatsobjectfromChatRoomActor.

Let'sstartcodingthisprotocol.First,wewillneedtodefinethemessagesthatwillbeexchangedbetweentheseActors,asshowninthefollowingpieceofcode:

packageactors

caseclassChatMessage(name:String,text:String)

caseclassStats(users:Set[String])

objectJoinChatRoom

objectTick

objectGetStats

Asyoucanseehere,wehaveaChatMessageclasswithanameandatext.Thiswillbethemessageeachuserwillsendonthechat.Then,wewillhaveastatsclass,whichhasasetofusers--thiswillbealltheusersloggedintothechatapplication.

Finally,wehavesomeactionmessages,suchasJoinChatRoom,Tick,andGetStats.So,JoinChatRoomwillbesentbyChatUserActortoChatRoomActorinordertojointhechat.TickwillbeascheduledmessagethatwillhappenfromtimetotimeinordertomakeChatBotAdminsendstatsaboutthechatroomtoallloggedusers.GetStatsisthemessagethatChatBotAdminActorwillsendtoChatRoomActorinordertogetinformationaboutwhoisintheroom.

Let'scodeourthreeactorsnow.

TheChatRoomActor.scalafileshouldlooksomethinglikethis:

packageactors

importakka.actor.Props

importakka.actor.Terminated

importakka.actor.ActorLogging

importakka.event.LoggingReceive

importakka.actor.Actor

importakka.actor.ActorRef

importplay.libs.Akka

importakka.actor.ActorSystem

classChatRoomActorextendsActorwithActorLogging{

Page 345: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

varusers=Set[ActorRef]()

defreceive=LoggingReceive{

casemsg:ChatMessage=>

usersforeach{_!msg}

caseJoinChatRoom=>

users+=sender

contextwatchsender

caseGetStats=>

valstats:String="onlineusers["+users.size+"]-users["

+users.map(a=>a.hashCode().mkString("|")+"]"

sender!stats

caseTerminated(user)=>

users-=user

}

}

objectChatRoomActor{

varroom:ActorRef=null

defapply(system:ActorSystem)={

this.synchronized{

if(room==null)room=system.actorOf(Props[ChatRoomActor])

room

}

}

}

ChatRoomActorhasavarcalledusers,whichisasetofActorRef.ActorRefisagenericreferencetoanyactor.Wehavethereceivefunctionwiththreecases:ChatMessage,JoinChatRoom,andGetStats.

AJoinChatRoomwillbesentbytheChatUserActormethodinordertojointheroom.Asyoucansee,wearegettingtheActorRefmethodfromthesenderActorusingthesender()function,andweareaddingthisreferencetothesetofusers.Inthisway,thesetofActorRefrepresentstheonlinelogged-inusersinthechatroomrightnow.

TheothercaseiswiththeChatMessagemethod.Basically,wewillbroadcastthemessagetoallusersinthechat.Wedothisbecausewehavethereferenceforallactorsinusers.Then,wewillcalltheforeachfunctioninordertoiterateallusersonebyone,andthenwewillsendthemessageusingFireAndForget"!"toeachuserActorrepresentedbytheoperatorunderscore_.

TheGetStatscasecreatesastringwithallchatroomstats.Fornow,thestatsarejustthenumberofonlineusers,whichiscomputedbycallingthesizefunctionontheusersobject.WearealsoshowingallthehashcodesthatidentifyallActorsloggedin,justforfun.

That'sourChatRoomActorimplementation.Asyoucansee,itishardtotalkaboutoneActorwithoutdescribingtheother,astheprotocolwillalwaysbekindofcoupled.YoumightalsobewonderingwhywehaveacompanionobjectfortheChatRoomActormethod.

ThisobjectistoprovideaneasywaytocreateActorinstances.Wearecreatingasingleroomforourdesign;wedon'twanttohavemultiplechatrooms,sothat'swhywewillneedto

Page 346: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

controlthecreationoftheroomActor.

Iftheroomisnull,wewillcreateanewroom;otherwise,wewillreturnthecachedinstanceoftheroomthatwealreadygotinthememory.WewillneedaninstanceoftheActorsysteminordertocreateactors,sothat'swhywearereceivingthesystemontheapplyfunction.TheapplyfunctionwillbecalledwhensomeonewritesacodelikeChatRoomActor(mySystem).

Now,let'smovetotheChatUserActorimplementation.

TheChatUserActor.scalafileshouldlooksomethinglikethis:

packageactors

importakka.actor.ActorRef

importakka.actor.Actor

importakka.actor.ActorLogging

importakka.event.LoggingReceive

importakka.actor.ActorSystem

importakka.actor.Props

classChatUserActor(room:ActorRef,out:ActorRef)extendsActorwith

ActorLogging{

overridedefpreStart()={

room!JoinChatRoom

}

defreceive=LoggingReceive{

caseChatMessage(name,text)ifsender==room=>

valresult:String=name+":"+text

out!result

case(text:String)=>

room!ChatMessage(text.split(":")(0),text.split(":")(1))

caseother=>

log.error("issue-notexpected:"+other)

}

}

objectChatUserActor{

defprops(system:ActorSystem)(out:ActorRef)=Props(new

ChatUserActor(ChatRoomActor(system),out))

}

ThisActorisalittlebiteasierthanthepreviousone.ChatUserActorreceives,asaparameter,theroomactorreferenceandalsoanoutactor.Theroomwillbeaninstanceoftheroomthattheuserwillusetocommunicatewithotherusers.TheActorRefmethodcalledoutisthePlayframeworkActorresponsibleforsendingtheanswerbacktothecontrollersandUI.

Weprettymuchjusthavetwocases:onewherewereceiveaChatMessageandtheotheristheChatUserActorsmethodinthechatroom.So,wewilljustneedtosendbacktotheUIusingtheoutActor.That'swhythereisafireandforgetmessagefortheoutActorwitharesult.UsinganewActormodelcanbedangerous,pleasereadmoreathttp://doc.akka.io/docs/akka/current/scala/actors.html.

ThereisanothercasethatjustreceivesastringthatwillbethemessagefromthatActoritself.RememberthateachActorrepresentsauserandabrowserfullofduplexconnectionsvia

Page 347: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WebSockets.Don'tworryaboutWebSocketsnow;wewillcoveritinmoredetaillaterinthischapter.

Forthiscasefunction,wearesendingtheChatMessagemethodtotheroom.Wewillsplitthemessagesintwoparts:theusernameandthetext,whichissplitby:.

Here,wealsohaveacompanionobjectforthesakeofgoodpractice.So,youcancallChatUserActor,passingtheActorsystemandacurriedparameterfortheoutactor.

Now,wewillmovetothelastActor:theBotAdminActor,whichshouldlooksomethinglikethis:

packageactors

importakka.actor.ActorRef

importakka.actor.Actor

importakka.actor.ActorLogging

importakka.event.LoggingReceive

importakka.actor.ActorSystem

importakka.actor.Props

importscala.concurrent.duration._

classChatBotAdminActor(system:ActorSystem)extendsActorwith

ActorLogging{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

valroom:ActorRef=ChatRoomActor(system)

valcancellable=system.scheduler.schedule(0seconds,

10seconds,self,Tick)

overridedefpreStart()={

room!JoinChatRoom

}

defreceive=LoggingReceive{

caseChatMessage(name,text)=>Unit

case(text:String)=>room!ChatMessage(text.split(":")(0),

text.split(":")(1))

caseTick=>

valresponse:String="AdminBot:"+ActorHelper.get

(GetStats,room)

sender()!response

caseother=>

log.error("issue-notexpected:"+other)

}

}

objectChatBotAdminActor{

varbot:ActorRef=null

defapply(system:ActorSystem)={

this.synchronized{

if(bot==null)bot=system.actorOf(Props

(newChatBotAdminActor(system)))

bot

}

}

}

Asyoucansee,thisActorreceivesthereferenceofthechatroomasaparameter.Usingthe

Page 348: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Actorsystem,itgetsthereferenceofthechatroomActor.ThisActorreceivesanActorSystemmessagebynow.

UsingtheActorsystemvariablecalledsystem,wewillalsoscheduleaTickforthisActorforeverytenseconds.Thistime,thewindowintervalwillbethetimeinwhichthebotwillnotifythechatroomaboutthecurrentstatus.

WewillalsooverridethepreStartfunction.AkkawillcallthisfunctionwhentheActoriscreatedontheactorsystem.Thisimplementationwillsendamessagetotheroom,whichisJoinChatRoom.

LikeallActors,thereisthereceivefunctionimplementation.FirstcasewithChatMessageisreturningUnit.Ifyouwanttomakethisbotrespondtopeople,removeUnitandwritetheproperScalacodeasyouwish.

Inthesecondcase,wewillhavetheStringmessagethatwillbesenttothechatroom.Finally,afterthiscase,wewillhavetheTickmethod,whichwillappeareverytenseconds.So,wewillusetheActorHelpertogetthestatsfromtheroom,andthenwewillsendastringmessagewiththeinformationabouttheroom.Thiswilltriggerthesecondcaseandbroadcastthemessagetothewholeroom.

Finally,wehaveacompanionobject.Wedon'twanttohavetwoinstancesofthebot,whichiswhywewillcontrolthisobjectcreationbydesign.We'redonewiththeactorsimplementations.Next,wewillneedtoworkanewcontrollerforthechatactors.

Page 349: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThechatcontrollerWewillneedtocreateanewcontroller.ThiscontrollerwillbelocatedatReactiveWebStore/app/controllers.

Page 350: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ImplementingthechatcontrollerChatController.scalashouldlooksomethinglikethis:

packagecontrollers

importakka.actor.ActorSystem

importakka.stream.Materializer

importjavax.inject._

importplay.api._

importplay.api.mvc._

importplay.api.libs.streams._

importactors.ChatUserActor

importactors.ChatBotAdminActor

@Singleton

classChatController@Inject()(implicitvalsystem:ActorSystem,

materializer:Materializer)

extendsController{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

ChatBotAdminActor(system)

defindex_socket=Action{request=>

Ok(views.html.chat_index()(Flash(Map())))

}

defws=WebSocket.accept[String,String]{request=>

ActorFlow.actorRef(out=>ChatUserActor.props(system)(out))

}

}

TheChatControllermethodwilluseGoogleGuicetogetinjectedinstancesofActorSystemandanActormaterializerinstance.AmaterializerisneededbecauseitwillprovidetheinstanceoftheoutActorforeachuserinthesystem.

Asyoucansee,wewillcreateaninstanceoftheChatBotAdminmethodpassingthroughtheactorsystem,whichGoogleGuiceinjectedforus.Forthiscontroller,wewilljusthavetwofunctions:onefunctiontorenderthechatUI,andtheotheronetoservetheWebSocket.

ThePlayframeworkalreadyprovidesbuilt-inintegrationwithAkkaandWebSockets.So,wewilljustneedtousetheActorFlowmethodusingtheactorReffunctioninordertoobtainanoutActor.

Here,wewillcalltheChatUserActorcompanionobjectandcreateachatuserforthewebsocketpassingouttheActorsystemthecontrollerhas.Asyoucansee,thisreturnsWebSocket.accept,whichisafullduplexconnectionbetweenthewebbrowserandthebackend.

Page 351: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringtheroutesNext,wewillneedtoexposeourcontrollerfunctionstotheUI.WewillneedtoaddmoreroutestotheReactiveWebStore/conf/routesfile:

routes

#

#AkkaandWebsockets

#

GET/chat/index_socketcontrollers.ChatController.index_socket

GET/chat/wscontrollers.ChatController.ws

Theroutesaredonenow.

Page 352: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WorkingontheUINow,itistimetocodeontheUIonboththeHTMLlayoutandtheWebSocketcodeinJavaScript.Wewillneedtocreateanewfile,locatedatReactiveWebStore/app/views.

Yourchat_index.scala.htmlfileshouldlooksomethinglikethis:

@()(implicitflash:Flash)

@main("Chat"){

<!DOCTYPEhtml>

<metacharset="utf-8"/>

<title>ChatRoom</title>

<scripttype="text/javascript">

varoutput;

varwebsocket=newWebSocket("ws://localhost:9000/chat/ws");

functioninit(){

output=document.getElementById("output");

websocket.onmessage=function(evt){

writeToScreen('<spanstyle="color:blue;">'+evt.data+

'</span>');

};

websocket.onerror=function(evt){

writeToScreen('<spanstyle="color:red;">ERROR:</span>'+

evt.data);

};

}

functiondoSend(message){

websocket.send(message);

}

functionwriteToScreen(message){

varpre=document.createElement("p");

pre.style.wordWrap="break-word";

pre.innerHTML=message;

$('#output').prepend(pre);

}

window.addEventListener("load",init,false);

</script>

<h3>Messages</h3>

<divid="output"style="width:800px;height:250px;overflow-y:

scroll;">

</div>

<divid="contentMessage">

<BR>

user:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<inputtype="text"name="txtUser"id="txtUser"/><BR><BR>

message:<inputtype="text"name="txtMessage"

id="txtMessage"/>

<BR>

<BR>

<ahref="#"class="btnbtn-success"

onclick="doSend(document.getElementById('txtUser').value+':'

+document.getElementById('txtMessage').value);">

<iclass="icon-plusicon-white"></i>SendMessage</a>

</div>

Page 353: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

}

TheUIisverysimple.Thereisaninputtextforyoutoputyournameandthereisanotheroneforthetextmessageitself,andasendbutton.AsyoucanseeintheJavaScriptcode,thefirstthingthatwewilldoisopenaWebSocketconnectiontothews://localhost:9000/chat/wsURL.Then,wewillregistertheinitfunctiontorunoncethebrowserisready.

TheinitfunctioninJavaScriptwillcreatetwofunctionsforourWebSocket.OnefunctionwillrunwhenanyerroroccursandtheotherfunctionwillrunforeachmessageemittedbytheAkkabackend.

WewillhaveadoSendfunctioninJavaScriptinordertosendamessagetotheWebSocket.ThismessagewillbedeliveredtothecontrollerandthentotheAkkaactors.YoucanalsoseesomeJQueryandHTMLcodeinordertocreatenewelementsontheUI.Thisisdoneinordertodisplaythemessageinthechatroom.

OK,thereisonemorethingthatwewillneedtodo-addareferencetothechatUIonthemainpageofourapplication.

YourIndex.scala.htmlshouldlooksomethinglikethis:

@(message:String)(implicitflash:Flash)

@main("WelcometoReactiveWebStore"){

<divclass="row-fluid">

<BR>

<divclass="span12">

<divclass="row-fluid">

<divclass="span6">

<ahref="/product"><imgheight="42"width="42"

src="@routes.Assets.at("images/product.png")">Manage

Products</a><BR>

<ahref="/review"><imgheight="42"width="42"

src="@routes.Assets.at("images/review.png")">Manage

Reviews</a><BR>

<ahref="/image"><imgheight="42"width="42"

src="@routes.Assets.at("images/image.png")">Manage

Images</a><BR>

</div>

<divclass="span6">

<ahref="/reports"><imgheight="42"width="42"

src="@routes.Assets.at("images/reports.png")">Reports</a>

<BR>

<ahref="/chat/index_socket"><imgheight="42"width="42"

src="@routes.Assets.at("images/chat.png")">ChatRoom</a>

<BR>

</div>

</div>

</div>

</div>

}

Page 354: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

WewillalsousetheopportunitytoimprovealittlebitoftheUIdesignusingTwitterBootstrapcolumndesign.Inthelastrow,youcanseeourlinktothechatUI.Now,wecanruntheapplicationandseeourchatworking.Run$activatorrun:

Asyoucansee,ournewchatUIlinkisthere.Now,let'shavefunwiththisnewfeature.Openfournewbrowsers(simulatefourdifferentusers),thengotothehttp://localhost:9000/chat/index_socketurlandlet'shavealittlechat.Youshouldseesomethingsimilartothis:

Page 355: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Almostdone.Ourchatfeatureworks;however,wewillneedtodomorethanjustafunctionalblackboxtestontheUI.Wewillneedunittests.Luckilyforus,wehavetheAkkatestkit,whichallowsustoeasilytestactors.

Page 356: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingAkkatestsWewillcreatethreemoretests:oneforeachactorthatwehave.TheyarelocatedatReactiveWebStore/test/.

Page 357: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalatestforAkkaActorChatUserActorSpec.scalashouldlooksomethinglikethis:

classOutActorextendsActor{

defreceive={

casea:Any=>Unit

}

}

classChatUserActorSpecextendsPlaySpec{

classActorsextendsTestKit(ActorSystem("test"))

"ChatUserActor"should{

"joinsthechatroomandsendamessage"innewActors{

valprobe1=newTestProbe(system)

valactorOutRef=TestActorRef[OutActor](Props[OutActor])

valactorRef=TestActorRef[ChatUserActor]

(ChatUserActor.props(system)(actorOutRef))

valuserActor=actorRef.underlyingActor

assert(userActor.context!=null)

valmsg="testUser:testmsg"

probe1.send(actorRef,msg)

actorRef.receive(msg)

receiveOne(2000millis)

}

}

}

TheAkkatestkitisverycoolasitallowsustotestactorswithaveryeasyDomainSpecificLanguage(DSL).It'spossibletochecktheActormailbox,theActorinternalstate,andsomuchmore.Thereisonetrickthatwewillneedtodobecauseweneedtoextendoneclass;inordertohavePlayworkingwiththeScalatest,wewillneedtousePlaySpec.However,wewillalsoneedtoextendoneclasstomaketheAkkatestkitwork,whichisTestKit.Wecan'textendbothatthesametime,butnoworries,thereisalwaysaworkaround.

Theworkaroundhereistocreateacaseclass,makethatcaseclassextendTestKit,andthenuseitinaspeccontext,thatis,inanewActor{}.

Here,wearecheckingifChatUserActorcanjointhechatroomproperly.ThisisdonebysimplycreatingtheActor,astheActorhasapreStartmethodthatwillauto-jointheroom.

WewillneedtocreateafakeimplementationoftheoutActorhere,whichiswhywehavetheOutActorimplementation.Wewillcreateaprobetotesttheactorsystem,andwewillalsouseaspecialfunctiontotesttheActors,calledTestActorRef.ThisabstractionprovidesawaytoaccesstheActor'sstateviaactorRef.underlyingActor,andthisisusefulbecauseyoucanchecktheActorinternalstatetovalidatethecode.TherestofthecodeisnormalAkkaandScalatestcode.Let'smovetothenexttest.

Page 358: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ChatroomActortestTheChatRoonActorSpec.scalafileshouldlooksomethinglikethis:

classChatRoomActorSpecextendsPlaySpec{

classActorsextendsTestKit(ActorSystem("test"))

"ChatRoomActor"should{

"acceptjoinsthechatrooms"innewActors{

valprobe1=newTestProbe(system)

valprobe2=newTestProbe(system)

valactorRef=TestActorRef[ChatRoomActor]

(Props[ChatRoomActor])

valroomActor=actorRef.underlyingActor

assert(roomActor.users.size==0)

probe1.send(actorRef,JoinChatRoom)

probe2.send(actorRef,JoinChatRoom)

awaitCond(roomActor.users.size==2,100millis)

assert(roomActor.users.contains(probe1.ref))

assert(roomActor.users.contains(probe2.ref))

}

"getstatsfromthechatroom"innewActors{

valprobe1=newTestProbe(system)

valactorRef=TestActorRef[ChatRoomActor]

(Props[ChatRoomActor])

valroomActor=actorRef.underlyingActor

assert(roomActor.users.size==0)

probe1.send(actorRef,JoinChatRoom)

awaitCond(roomActor.users.size==1,100millis)

assert(roomActor.users.contains(probe1.ref))

probe1.send(actorRef,GetStats)

receiveOne(2000millis)

}

"andbroadcastmessages"innewActors{

valprobe1=newTestProbe(system)

valprobe2=newTestProbe(system)

valactorRef=TestActorRef[ChatRoomActor]

(Props[ChatRoomActor])

valroomActor=actorRef.underlyingActor

probe1.send(actorRef,JoinChatRoom)

probe2.send(actorRef,JoinChatRoom)

awaitCond(roomActor.users.size==2,100millis)

valmsg=ChatMessage("sender","testmessage")

actorRef.receive(msg)

probe1.expectMsg(msg)

probe2.expectMsg(msg)

}

"andtrackusersrefandcounts"innewActors{

valprobe1=newTestProbe(system)

valprobe2=newTestProbe(system)

valactorRef=TestActorRef[ChatRoomActor]

(Props[ChatRoomActor])

valroomActor=actorRef.underlyingActor

probe1.send(actorRef,JoinChatRoom)

probe2.send(actorRef,JoinChatRoom)

awaitCond(roomActor.users.size==2,100millis)

Page 359: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

probe2.ref!PoisonPill

awaitCond(roomActor.users.size==1,100millis)

}

}

}

So,herewehavethesameconceptsastheothertest.However,wehavemoreusageoftheAkkatestkitDSL.Forinstance,weareusingexpectMsgontheprobetocheckifanActorreceivedaspecificmessage.WearealsousingawaitCondtochecktheActor'sinternalstateinanassertion.

NowisthetimetotestthelastActormethod.

Page 360: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ChatBotAdminActortestChatBotAdminActorSpec.scalafileshouldlooksomethinglikethis:

classChatBotAdminActorSpecextendsTestKit(ActorSystem("test"))

withImplicitSender

withWordSpecLikewithMatcherswithBeforeAndAfterAll{

"ChatBotAdminActor"should{

"beabletocreateBotAdminintheChatRoomandTick"in{

valprobe1=newTestProbe(system)

valactorRef=TestActorRef[ChatBotAdminActor](Props(new

ChatBotAdminActor(system)))

valbotActor=actorRef.underlyingActor

assert(botActor.context!=null)

awaitCond(botActor.room!=null)

}

}

}

Forthistest,wewillcheckiftheactorcontextwasnotnull,andalsoiftheroomwascreatedandtheschedulerwasalsonotnull.Allgoodtogo.

Alright,that'sit!Thisisthelastactortest.Nowwearecompletelydone.Youcanrunthistestwith$activatortest,or,ifyouprefertheactivator,thenuse"test-onlyTESTCLASSNAME"-Dsbt.task.forcegc=falsetorunaspecifictestcase.

Page 361: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtoworkwithAkkaactorsandcreatedawebchatusingAkka,thePlayframework,andWebSockets.AkkaisareallypowerfulsolutionthatcanbeusedwithorwithoutthePlayframework.Additionally,youlearnedabouttheActormodel,mailboxes,routing,persistence,Akkaconfiguration,messagepatterns,andhowtowritecodewithactorsinScalaandPlay.

Inthenextchapter,youwilllearnmoreaboutREST,JSON,andhowtomodelaRESTAPI,aswellashowtocreateaScalaclientforyourRESTservices.

Page 362: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter9.DesignYourRESTAPIInthepreviouschapter,weaddedanewchatfeatureinourappusingAkka.Ourwebapplicationisclosetotheend.ThischapterwilladdtheRESTAPIinourPlayframeworkapplication.

WewillalsocreateaScalaclientusingthewslibraryfromthePlayframeworkinordertocallourRESTAPI.Laterinthischapter,wewilladdSwaggersupportandembedtheSwaggerUIinourapp.

Inthischapter,wewillcoverthefollowingtopics:

RESTandAPIdesignCreatingourAPIwithRESTandJSONCreatingaScalaclientAddingvalidationsAddingbackpressureAddingSwaggersupport

Page 363: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

IntroductiontoRESTRepresentationalStateTransfer(REST)isanarchitecturalstyle.ItwasdefinedbyRoyFieldinginhisdoctoraldissertation.RESThappensovertheHTTP1.1protocolusingHTTPverbs,suchasGET,POST,DELETE,PUT,andUniformResourceIdentifier(URI),forinstance,/users/profile/1orsales/cart/add/2.

TheRESTarchitecturehasthefollowingproperties:

Simplicity:PrettymuchalllanguageshavelibrariestomanipulateHTTPURIs.Interoperability:RESTislanguage,platform,andOSagnostic.ScalableandReliable:AsRESTisbasedonHTTP,youcanusetheHTTPservertoscaleupyourapplicationinconjunctionwithHTTPloadbalancer,theHTTPcaches,andHTTPDNS.SeparationofConcerns(SOC):AsyouhaveaURI,that'syourcontract,notthecode,underlyingbackend,ordatabase.Thismeansthatyoucanchangethedatabaseorlanguagewithoutaffectingthecode.Client/Server:ThereisaserverthatprovidestheRESTinterfaceandtheclients,whichcalltheRESTendpoints.

WebservicesthatembracetheRESTprinciplesareoftencalledRESTful.

Page 364: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RESTAPIdesignWhenyouareworkingwithREST,therearesomeprinciplesthatyoushouldkeepinmind,andtheseprinciplesshouldprovideguidanceforyourdesignchoiceswhenyouaredoingAPIdesign.

Page 365: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

HTTPverbsdesignThesearethefollowingverbsfoundinHTTP:

GET:ThisisoftenusedtoanswerqueriesPUT:ThisisoftenusedtoinsertdataPOST:ThisisoftenusedtoupdatedataDELETE:Thisisoftenusedtoremovedata

Whydowekeepsayingoften?Well,therearesomeexceptionsinregardsofsizelimitations.Forinstance,fortheGETverb,wecan'thavearequestbiggerthan8192bytesor8KB.Ifyouneedtosendabiggerpayload,wewillneedtousethePOSTverb.

Page 366: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

UniformAPIRESTusesauniformAPI.Forexample,considerthefollowingpieceofcode:

GET/users/1=Listinformationaboutuserid1

PUT/users/1=Insertuser1

POST/users/1=Updateuser1

DELETE/users/1=Deleteuser1

GET/users/=ListsAllusers

Ifwechangetheresourcefromuserstosales,theAPIwouldalmostbethesame.RetrievingdataisdoneusingGETandupdateisdoneviaPOST,soit'sauniformAPI.

Page 367: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ResponsewithHTTPstatuscodesRESTrunstheerrorhandlerusingtheHTTP1.1statuscodes.Forinstance:

200->OK:ThisisoftenusedwiththeGETverb201->Created:ThisisoftenusedbythePUT/POSTverbs204->NoContent:ThisisoftenfortheDELETEverb400->InvalidRequest:ThisoftenmeansaninvalidrequestforthePOST/PUTverbs404->NotFound:ThisisoftenusedwiththeGETverb500->InternalServerError-UnexpectedServerError:Thisisoftenusedbyalltheverbs

Page 368: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RESTAPIpatternsTherearesomecommonspatternsforgoodandclearRESTAPIdesigns,asfollows:

Usenouns;donotuseverbs:Often,youcanusestandardURIs,suchas/cars/or/members/.Youshouldnotuse/getCars/orgetMembers/becauseyouareusingtheURIwithaverb,andtheverbalreadytellstheactions.GETmethodshouldnotchangestate:Ifyouwanttochangethestateoftheserver,youwillneedtouseverbssuchasPUT,POST,orDELETE.GETshouldnotchangethestateoftheserver,soitshouldalwaysbesafecallingGETasmanytimesasyouwant.Thisiscalledidempotent.Prefersub-resourcerelation:Let'ssaywehavearesourcecalled/users/,andauserhasprojects.It'salwaysagoodideatousesub-resources,suchas/users/1/projects/2,becausewehavearelationshipbetweenusersandprojects.UseHTTPheaders:HTTPheadersshouldbeusedforserialization,security,andallthekindsofmetadatayourapplicationneeds.TheHTTPHeadersareoftenusedforcontentnegotiation.Forinstance,youmightdothefollowing:

HTTPHEADERContent-Type=XML-GET/cars/1

HTTPHEADERContent-Type=JSON-GET/cars/1

TheURIisthesame;however,basedontheheadertype,itwillreturndatainXMLorJSONformat.Filter,SortingandPagination:Sometimes,yourdatamaybebig.It'salwaysagoodideatoprovidemechanismstosort,filter,andpaginateasfollows:

GET/projects/1/tasks?by=priority->Sorting

GET/projects/1/tasks?status=done->Filter

GET/projects/1/tasks?limit=30&offset=5->Pagination

Page 369: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

APIversioningTherearetwowaystoperformAPIversioning.Firststrategyistoversionbytheendpointexplicitsuchas/v1/cars.Thesecondstrategyisbasedonmetadatasuchas/cars/,butthenyouwillpassanHTTPHEADERversionasv1.

Bothstrategieshaveprosandcons.Explicitversioningismoreclear,andyoucanalwayscreateanewversionanddon'tbreakyourconsumers.Headerstrategyismoreelegant;however,itcangettrickytomanage.

Page 370: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Someanti-patternstobeavoidedThereareseveraltrapsintheRESTAPIdesign,butthefollowingthingsneedtobeavoided:

GETverbforeverythingIgnoringHTTPheaderssuchasMIME-typesReturning200whenanerrorhappensReturning500foraninvalidparameteroramissingparameter

Page 371: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingourAPIwithRESTandJSONAlright,nowisthetimetodesignaRESTAPIforyourPlayframeworkapplication.WewillcreateanAPItoexportalldatainthesystem.ThisAPIwillbeREADonly;however,youcanaddwriteoperationsifyoulike.

Lateroninthischapter,wewilladdsomebackpressuretolimittheAPIRESTrateforconsumersandcreateaScalaclientapplicationforourRESTAPI.So,firstofall,let'sgetstartedwiththePlayframework(server)first.

Wedon'tneedanyextralibraryinordertocreateaRESTAPIinourPlayframeworkapplication.Wewilljustneedanewcontrollerandnewroutes.Additionally,wewillleveragemostofthecodewemadeinthepreviouschapters.

Page 372: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RestApiContollerLet'screateanewcontrollerlocatedatReactiveWebStore/app/controllers.

RESTAPIFrontControllerimplementation

RestApiController.scalafileshouldlooksomethinglikethis:

packagecontrollers

@Singleton

classRestAPIController@Inject()

(valproductService:IProductService,

valreviewService:IReviewService,

valimageService:IImageService)extendsController{

importplay.api.libs.concurrent.Execution.

Implicits.defaultContext

deflistAllProducts=Action{

valfuture=productService.findAll()

valproducts=Awaits.get(5,future)

valjson=ProductsJson.toJson(products)

Ok(json)

}

deflistAllReviews=Action{

valfuture=reviewService.findAll()

valreviews=Awaits.get(5,future)

valjson=ReviewsJson.toJson(reviews)

Ok(json)

}

defprocessImages={

valfuture=imageService.findAll()

valimages=Awaits.get(5,future)

valjson=ImagesJson.toJson(images)

json

}

deflistAllImages=Action{

Ok(processImages)

}

}

Basically,wehavethreefunctionshere.Thesefunctionslistallproducts,images,andreviews.Asyoucanseeatthetopofthecontroller,weareinjectingthethreeservicesthatwehaveforproducts,images,andreviews.

Thecodeisprettymuchstraightforwardforallfunctions.First,wewillcalltheproperservice,andthenwewillwaitfortheresultwiththeawaitobject.Oncewehavethedata,wewillcallafunctiontoconvertthedatatoJSON.

Let'stakealookattheJSONhelpersobjectsthatweusedhere.

Page 373: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

JSONmappingOurRESTcontrollerusedJSONhelperobjectstomapobjectstoJSON.First,wewillstartwiththeProductsJSONhelper.

ProductsJsonislocatedatReactiveWebStore/app/controllers/Product.scala:

objectProductsJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalproductWrites:Writes[Product]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"name").write[String]and

(JsPath\"details").write[String]and

(JsPath\"price").write[BigDecimal]

)(unlift(Product.unapply))

implicitvalproductReads:Reads[Product]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"name").read[String]and

(JsPath\"details").read[String]and

(JsPath\"price").read[BigDecimal]

)(Product.apply_)

deftoJson(products:Option[Seq[Product]])=Json.toJson(products)

}

Basically,therearethreeimportantconceptshere.First,wehaveproductsWrites,whichmapsfromJSONtomodel,andProductforwrites,whichisalsoknownasdeserialization.WehaveanothermappingforserializationcalledproductsReads,whichconvertsobjectstoJSON.

Asyoucansee,weneedtomapallfieldsexistinginourmodel,suchasID,name,details,andprice.Thismappingmustmatchpropertypesaswell.IDmappingusesreadNullablebecauseIDisoptional.

Finally,wehaveafunctiontoconvertfromJSONtoobject,calledtoJson,whichusesagenericPlayframeworklibrarycalledJSON.Let'smoveforthenexthelper--thereview.

ReviewsJsonislocatedatReactiveWebStore/app/controllers/Review.scalaandshouldlooksomethinglikethis:

objectReviewsJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalreviewWrites:Writes[Review]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"productId").write[Option[Long]]and

(JsPath\"author").write[String]and

(JsPath\"comment").write[String]

)(unlift(Review.unapply))

Page 374: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

implicitvalreviewReads:Reads[Review]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"productId").readNullable[Long]and

(JsPath\"author").read[String]and

(JsPath\"comment").read[String]

)(Review.apply_)

deftoJson(reviews:Option[Seq[Review]])=Json.toJson(reviews)

}

Here,wehavethesameconceptsthatwesawearlierintheProductsJSONhelper.Wehaveamappingforreadsandwritesandafunctionwhichconvertsamodel.ReviewtoJSON.Let'smovetothelasthelper,theImageJson.

ImagesJsonislocatedatReactiveWebStore/app/controllers/Image.scala,whichshouldlooksomethinglikethis:

objectImagesJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalimagesWrites:Writes[Image]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"productId").write[Option[Long]]and

(JsPath\"url").write[String]

)(unlift(Image.unapply))

implicitvalimagesReads:Reads[Image]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"productId").readNullable[Long]and

(JsPath\"url").read[String]

)(Image.apply_)

deftoJson(images:Option[Seq[Image]])=Json.toJson(images)

}

Justaswiththeothertwomappers,wehavereads,writes,mappings,andthetoJsonfunction.Wearedonewithmappers,sonowthenextstepistocreatethenewroutes.

Page 375: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ConfiguringnewroutesWeneedtoaddthefollowingthreenewroutesforourRESTAPI,whichislocatedatReactiveWebStore/conf/routes:

#

#RESTAPI

#

GET/REST/api/product/all

controllers.RestAPIController.listAllProducts

GET/REST/api/review/all

controllers.RestAPIController.listAllReviews

GET/REST/api/image/allcontrollers.RestAPIController.listAllImages

Asyoucansee,wemappedallthelistoperationswejustcreated.

Page 376: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

TestingtheAPIusingthebrowserNowwecanrun$activatorrunandtestournewRESTAPIusingourwebbrowser.

Gotohttp://localhost:9000/REST/api/product/all;youshouldseesomethingsimilartothefollowingscreenshot:

Let'slookatthereviewAPI.

Gotohttp://localhost:9000/REST/api/review/all;youshouldseeresultssimilartothefollowingscreenshot:

Finally,let'scheckouttheimageofRESTAPI.

Page 377: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Gotohttp://localhost:9000/REST/api/image/all;youshouldseeresultssimilartothefollowingscreenshot:

OK.NowwewillcontinuetoworkwithREST.Wejustfinishedtheserver;however,itisimportanttocreateaRESTclienttoconsumetheseRESTAPIs.

Page 378: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingaScalaclientFirst,youwillneedtocreateanewproject.Gotoyourfilesystemandcreateafoldercalledrest-client.Then,createanotherfolderinsiderest-clientcalledproject.Insideproject,youwillneedtoaddthefollowingtwofiles:

build.properties:ThiscontainsanSBTconfiguration,suchasversionPlugins.sbt:ThiscontainsanSBTpluginsconfiguration

Let'sstartwithbuild.properties:

sbt.version=0.13.11

Asyoucanseehere,weareconfiguringthisprojecttouseSBTversion0.13.11.Now,wecanmovetothepluginsfile.

Page 379: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Configuringplugins.sbtYourplugins.sbtfileshouldlooksomethinglikethis:

addSbtPlugin("com.typesafe.sbteclipse"%"sbteclipse-plugin"%

"2.5.0")

addSbtPlugin("com.github.mpeltonen"%"sbt-idea"%"1.6.0")

Here,weareaddingEclipseandIntelliJsupport.Forthisbook,weareusingEclipse,butfeelfreetouseanythingyoulike.

Outsideoftheprojectfolder,underrest-client,wewillneedtoconfigurethebuild.sbtfile.

Page 380: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Configuringbuild.sbtYourbuild.sbtfileshouldlooksomethinglikethis:

name:="rest-client"

version:="1.0"

scalaVersion:="2.11.7"

scalaVersioninThisBuild:="2.11.7"

resolvers+=DefaultMavenRepository

resolvers+=JavaNet1Repository

resolvers+="OSSSonatype"at

"https://oss.sonatype.org/content/repositories/releases"

resolvers+="SonatypeOSSSnapshots"at

"https://oss.sonatype.org/content/repositories/snapshots"

resolvers+="SonatypeOSSSnapshots"at

"https://oss.sonatype.org/content/repositories/snapshots"

resolvers+="amateras-repo"at

"http://amateras.sourceforge.jp/mvn/"

libraryDependencies+="com.typesafe.play"%"play-ws_2.11"%

"2.5.6"

libraryDependencies+="org.scalatest"%"scalatest_2.11"%"2.2.6"

%Test

Sohere,weareusingScalaversion2.11.7,andwearedeclaringjusttwodependencies.Onedependencyisfortests,whichisscala-test,andtheotherdependencyisonthePlayframeworkwslibrary,whichwewillusetocallourRESTAPIs.

Let'salsocreatetwosourcefolders,asfollows:

src/main/scala:ThisistheScalasourcecodesrc/test/scala:ThisistheScalatestsourcecode

OK.Nowwecanrun$sbtcleancompileeclipseinordertodownloadthedependenciesfromthewebandcreatealltheEclipseprojectfilesthatweneed.

NowwecanimportthiscodeinEclipseandmoveon.

Page 381: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalaclientcodeFirstofall,wewillneedtocreateaFactorytoinstantiatetheWSPlayframeworklibrarytocallwebservices.Undertherest-client/src/main/scalalocation,let'screateapackagecalledclientandaddthefollowingcodeunderWSFactory.scala:

packageclient

objectWSFactory{

importakka.actor.ActorSystem

importakka.stream.ActorMaterializer

defws={

implicitvalsystem=ActorSystem()

implicitvalmaterializer=ActorMaterializer()

importcom.typesafe.config.ConfigFactory

importplay.api._

importplay.api.libs.ws._

importplay.api.libs.ws.ahc.{AhcWSClient,AhcWSClientConfig}

importplay.api.libs.ws.ahc.AhcConfigBuilder

importorg.asynchttpclient.AsyncHttpClientConfig

importjava.io.File

valconfiguration=Configuration.reference++

Configuration(ConfigFactory.parseString(

"""

|ws.followRedirects=true

""".stripMargin))

valparser=newWSConfigParser(configuration,

play.api.Environment.simple(

newFile("/tmp/"),null))

valconfig=newAhcWSClientConfig(wsClientConfig=

parser.parse())

valbuilder=newAhcConfigBuilder(config)

vallogging=new

AsyncHttpClientConfig.AdditionalChannelInitializer(){

overridedefinitChannel(channel:io.netty.channel.Channel):

Unit={

channel.pipeline.addFirst("log",new

io.netty.handler.logging.LoggingHandler("debug"))

}

}

valahcBuilder=builder.configure()

ahcBuilder.setHttpAdditionalChannelInitializer(logging)

valahcConfig=ahcBuilder.build()

newAhcWSClient(ahcConfig)

}

}

Theprecedingcodeisjusttechnical.ThesearethestepsneededtoinstantiatetheWSClientoutsidethePlayframework.IfthisclientwasawebapplicationusingthePlayframework,itwouldbewayeasieraswecanjustuseGoogleGuiceandinjectwhatweneed.

ThemainideayouneedtokeepinmindisthatyouneedtouseAkkaandActorSysteminordertousethisfeature.Asyoucansee,allthiscodeislockedinsideanobjectinasinglefunctioncalledws.

Page 382: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Wewillneedsomeutilityclasstoworkwithfutures.AsweusewslibrarytocallRESTAPIs,itreturnsFuture.So,let'screateanewpackagecalledutils:

YourAwaits.scalafileshouldlooklikesomethinglikethis:

packageutils

importscala.concurrent.Future

importscala.concurrent.duration._

importscala.concurrent.Await

objectAwaits{

defget[T](sec:Int,f:Future[T]):T={

Await.result[T](f,secseconds)

}

}

Theprecedingcodeisprettysimple.WeusedtheAwaitobjectandthenusedagenericTinordertoconverttheresulttoagenericparameterizedtype.Byusingthisparameter,wewillalsoreceivehowmanysecondsweshouldwaitbeforethetimeout.

Page 383: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingourRESTclientproxiesWewillnowmakeRESTcalls;however,wewillcreateaScalaAPI.So,thedeveloperswhouseourrest-clientwon'tneedtodealwithRESTandjustexecutetheScalacode.Thisisgoodformanyreasons,someofwhichareasfollows:

SOC:WestillhaveseparationofconcernsbetweenthePlayframeworkandtheclientapplicationIsolation:IftheRESTAPIchanges,wewillneedtodealwithitontheproxylayerAbstraction:TherestoftheclientcodejustusesScalaanddoesnotknowanythingaboutRESTorHTTPcalls

Thesetechniquesareverycommonnowadayswithmicroservices.Thesetechniquescanalsobeknownasdriversorthickclients.Rightnow,wewillneedtocreatethreeproxies,oneforeachresource,thatwehaveontheRESTAPI.Let'screateanewpackagecalledproxy.

YourProductProxy.scalafileshouldlooksomethinglikethis:

packageproxy

importclient.WSFactory

importutils.Awaits

caseclassProduct

(varid:Option[Long],

varname:String,

vardetails:String,

varprice:BigDecimal){

overridedeftoString:String={

"Product{id:"+id.getOrElse(0)+",name:"+name+",

details:"+details+",price:

"+price+"}"

}

}

objectProductsJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalproductWrites:Writes[Product]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"name").write[String]and

(JsPath\"details").write[String]and

(JsPath\"price").write[BigDecimal]

)(unlift(Product.unapply))

implicitvalproductReads:Reads[Product]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"name").read[String]and

(JsPath\"details").read[String]and

(JsPath\"price").read[BigDecimal]

)(Product.apply_)

deftoJson(products:Option[Seq[Product]])=Json.toJson(products)

}

objectProductProxy{

importscala.concurrent.Future

Page 384: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

importplay.api.libs.json._

importProductsJson._

valurl="http://localhost:9000/REST/api/product/all"

implicitvalcontext=

play.api.libs.concurrent.Execution.Implicits.defaultContext

deflistAll():Option[List[Product]]={

valws=WSFactory.ws

valfutureResult:Future[Option[List[Product]]]=

ws.url(url).withHeaders("Accept"->

"application/json").get().map(

response=>

Json.parse(response.body).validate[List[Product]].asOpt

)

valproducts=Awaits.get(10,futureResult)

ws.close

products

}

}

Wehavethreebigconceptsinthiscode.Firstofall,wehaveacaseclassthatrepresentstheproduct.TheprecedingcodeisverysimilartothecodewehaveonthePlayframeworkapplication.However,ifyoupayattention,youwillseeitismuchcleanerbecausewedon'thaveanymetadataaroundpersistence.

Youmightthink,thisisduplicatedcode!Itis,anditis100%okay.Duplicatecodeisdecoupled.RememberthatwehaveaRESTinterfaceandalsoaproxybetweentherestoftheclientcode,sowehaveatleasttwolayersofindirectionthatwecandealwithchanges.Ifthesetwocodebasessharethesameclass,wewouldhavecouplingandlessspacetoaccommodatechanges.

Thesecondbigconcepthereismapping.WewillreceiveJSON,andwewillwanttoconvertJSONtoourcaseclass,sowewillhavesimilarmappingthatwedidinthePlayframeworkapplication.

Finally,wehavetheproxyimplementation.WewillinstantiatethePlayframeworkWSlibraryusingourfactoryandcallthewsfunction.Then,wewillusetheurlfunctionpassingtheRESTAPIURIforproductsanddefineaheaderinordertoacceptJSON.WearealsodoingthisusingtheHTTPverb,GET.TheresponseismappedwithJson.parsepassingresponse.body.Additionally,wewillcallthevalidatefunctiontomakesurethisJSONmatchesourcaseclass.Thisvalidationisimportantbecausethenwecanbesurethattheformatdidnotchange,andthateverythingworksfine.WSwillreturnthisasaFuture,sowewilluseourAwaitshelpertogettheresult.

Let'smovetothenextproxy,thereview.

YourReviewProxy.scalafileshouldlooksomethinglikethis:

packageproxy

importclient.WSFactory

importutils.Awaits

Page 385: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

caseclassReview

(varid:Option[Long],

varproductId:Option[Long],

varauthor:String,

varcomment:String)

{

overridedeftoString:String={

"Review{id:"+id+",productId:"+productId.getOrElse(0)

+",author:"+author+

",comment:"+comment+"}"

}

}

objectReviewsJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalreviewWrites:Writes[Review]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"productId").write[Option[Long]]and

(JsPath\"author").write[String]and

(JsPath\"comment").write[String]

)(unlift(Review.unapply))

implicitvalreviewReads:Reads[Review]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"productId").readNullable[Long]and

(JsPath\"author").read[String]and

(JsPath\"comment").read[String]

)(Review.apply_)

deftoJson(reviews:Option[Seq[Review]])=Json.toJson(reviews)

}

objectReviewProxy{

importscala.concurrent.Future

importplay.api.libs.json._

importReviewsJson._

valurl="http://localhost:9000/REST/api/review/all"

implicitvalcontext=

play.api.libs.concurrent.Execution.Implicits.defaultContext

deflistAll():Option[List[Review]]={

valws=WSFactory.ws

valfutureResult:Future[Option[List[Review]]]=

ws.url(url).withHeaders("Accept"->

"application/json").get().map(

response=>

Json.parse(response.body).validate[List[Review]].asOpt

)

valreviews=Awaits.get(10,futureResult)

ws.close

reviews

}

}

Here,wehavethesameprinciplesthatwehadontheproductproxy,butthistimeforreview.Asyoucansee,wewillcalladifferentURI.Now,let'smovetothelastproxy--theImageProxy.scalafile.

Page 386: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

YourImageProxy.scalafileshouldlooksomethinglikethis:

packageproxy

importclient.WSFactory

importutils.Awaits

caseclassImage

(varid:Option[Long],

varproductId:Option[Long],

varurl:String)

{

overridedeftoString:String={

"Image{productId:"+productId.getOrElse(0)+",url:"+url

+"}"

}

}

objectImagesJson{

importplay.api.libs.json._

importplay.api.libs.json.Reads._

importplay.api.libs.functional.syntax._

implicitvalimagesWrites:Writes[Image]=(

(JsPath\"id").write[Option[Long]]and

(JsPath\"productId").write[Option[Long]]and

(JsPath\"url").write[String]

)(unlift(Image.unapply))

implicitvalimagesReads:Reads[Image]=(

(JsPath\"id").readNullable[Long]and

(JsPath\"productId").readNullable[Long]and

(JsPath\"url").read[String]

)(Image.apply_)

deftoJson(images:Option[Seq[Image]])=Json.toJson(images)

}

objectImageProxy{

importscala.concurrent.Future

importplay.api.libs.json._

importImagesJson._

valurl="http://localhost:9000/REST/api/image/all"

implicitvalcontext=

play.api.libs.concurrent.Execution.Implicits.defaultContext

deflistAll():Option[List[Image]]={

valws=WSFactory.ws

valfutureResult:Future[Option[List[Image]]]=

ws.url(url).withHeaders("Accept"->

"application/json").get().map(

response=>

Json.parse(response.body).validate[List[Image]].asOpt

)

valimages=Awaits.get(10,futureResult)

ws.close

images

}

}

That'sit.Wehavethesameconceptsasproductandreview.Wehavefinishedallourproxies.Now,itistimetotestourproxyimplementation.Thebestwaytodothisisviatests,solet'screateScalatestsforthesethreeimplementations.

Page 387: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CreatingScalaTesttestsfortheproxiesUnderthe/src/test/scalasourcefolder,wewillneedtocreateapackagecalledproxy.test.

YourProductProxtTestSpec.scalashouldlooksomethinglikethis:

packageproxy.test

importorg.scalatest._

importproxy.ProductProxy

classProductProxtTestSpecextendsFlatSpecwithMatchers{

"AProductRestproxy"should"returnallproducts"in{

valproducts=ProductProxy.listAll().get

productsshouldNot(be(null))

productsshouldNot(be(empty))

}

}

Thetestisquitesimple;wewilljusthavetocallthelistAlloperationinourproductproxyandthenaddsomeassertionstomakesuretheresultisnotnull.Wewillalsoshowalltheproductsintheconsole.

Now,wewillneedtocreatetestsforthereviewproxy,whichwillbesimilartotheproduct.

YourReviewProxyTestSpec.scalafileshouldlooksomethinglikethis:

packageproxy.test

importorg.scalatest._

importproxy.ReviewProxy

classReviewProxyTestSpecextendsFlatSpecwithMatchers{

"AReviewRESTProxy"should"returnallreviews"in{

valreviews=ReviewProxy.listAll().get

reviewsshouldNot(be(null))

reviewsshouldNot(be(empty))

for(r<-reviews){

println(r)

}

}

}

Here,weusedtheproxyideastotestthereview.WecalledtheproxyusingthelistAllfunctiontogetallthereviews.Later,wewillchecktoseeifthereviewisnotnull.Wewillprintallthereviews.Finally,it'stimetomovetothelastproxytest--theimageproxy.

YourImageProxyTestSpec.scalashouldlooksomethinglikethis:

packageproxy.test

importorg.scalatest._

importproxy.ImageProxy

importscala.concurrent.Future

importplay.api.libs.concurrent.Execution.Implicits.defaultContext

importjava.util.concurrent.CountDownLatch

classImageProxyTestSpecextendsFlatSpecwithMatchers{

Page 388: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

"AImageRESTProxy"should"returnallimages"in{

valimages=ImageProxy.listAll().get

imagesshouldNot(be(null))

imagesshouldNot(be(empty))

for(i<-images){

println(i)

}

}

}

Samedealgoesfortheimageproxy.Wehaveallourtests;now,wecanrunthetests.YouwillneedtomakesureourReactiveWebStorePlayframeworkappisupandrunning.

Let'srunthistestwithsbt:

openyourconsoleandtypein$sbttest

Alright,itallworks!Ournextstepwillbetoaddbackpressure.

Page 389: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingbackpressureBackpressureisawell-knownconceptintheautomotiveindustry.Nowadays,thistermisusedinsoftwareengineeringaswell.Backpressureintheautomotiveworldreferstothepressureopposedtothedesiredflowofgassesinaconfinedplace,suchasapipe.Forsoftwareengineering,itisoftenrelatedtoslowingdownaproducer,whichcanbeanapplication,astreamprocessingengine,oreventheuseritself.

WhenweareexecutingREST,it'seasytoreachasituationwheretheclientcansaturatetheserver.Thiscanbeasecuritybreachtoo,whichisalsoknownastheDenialOfService(DOS)attack.

Therearetwoarchitecturalscenarios.Inthefirstscenario,yourRESTAPIisinternal,andyoujusthaveconsumersinyourcompany.Inthesecondscenario,youaremakingtheRESTAPIapublicAPIsothatitwillbeopentothewholeInternet.Forthisscenario,youreallyshouldhavebackpressure,alsoknownasthrottling.

It'spossibletoscaleourarchitectureinordertohandlemoreusers.Wewilldiscussthis,andthescalabilitytechniques,inChapter10,ScalingUp.

Currently,thereareseveralwaystoapplybackpressure.Forinstance,ifourcodeispureRxScala/RxJava,wecanapplybackpressureonobservables.Moredetailscanbefoundathttps://github.com/ReactiveX/RxJava/wiki/Backpressure.

AsweareexposingaRESTinterface,wewilladdbackpressureonthecontroller,sowewillneedtocreateanewclasswiththebackpressurecode.

Therearesomealgorithmsforbackpressure;wewillusetheleakybucketalgorithm.Thealgorithmitselfisverysimple--just30linesofScalacode.

Theleakybucketalgorithm

Theleakybucketmetaphorisprettysimple.Let'stakealookatitinthefollowingdiagram:

Page 390: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About
Page 391: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Themetaphorbehindthealgorithmisbasedaroundabucketwithholes.Thewaterflowsordripsintothebucketandleaksthroughtheholesofthebucket.Ifthereistoomuchwater,andthebucketisfullofwater,thewaterwillspilloutofthebucket--inotherwords,itwillbediscarded.

Thisalgorithmisusedinnetworkprograming,andalsobythetelecommunicationindustry.TheAPImanagersolutionsarealsousecasesforthisalgorithm.

ThisconceptallowsRATElimitconstraints.Wecanexpressthebackpressureratelimitsinrequestspertime.Time,inthiscase,isoftenmeasuredinsecondsorminutes,sowehaveRequestsPerSecond(RPS)orRequestsPerMinute(RPM).

Youcanimplementthisalgorithmwithaqueue.However,inourimplementation,wewillnotuseaqueue;wewillusetimeinordertocontroltheflow.Ourimplementationwillalsobelockfree,ornon-blocking,aswewon'tusethreadsorexternalresources.

NowisthetimetocodealeakybucketinScala.Firstofall,wewillcreatethiscodefortheReactiveWebStoreapplication.WewillneedtocreateanewpackagelocatedatReactiveWebStore/app.Thenewpackagenamewillbebackpressure.

Scalaleakybucketimplementation

YourLeakyBucket.scalafileshouldhavethefollowingfiles:

packagebackpresurre

importscala.concurrent.duration._

importjava.util.Date

classLeakyBucket(varrate:Int,varperDuration:FiniteDuration){

varnumDropsInBucket:Int=0

vartimeOfLastDropLeak:Date=null

varmsDropLeaks=perDuration.toMillis

defdropToBucket():Boolean={

synchronized{

varnow=newDate()

if(timeOfLastDropLeak!=null){

vardeltaT=now.getTime()-timeOfLastDropLeak.getTime()

varnumberToLeak:Long=deltaT/msDropLeaks

if(numberToLeak>0){

if(numDropsInBucket<=numberToLeak){

numDropsInBucket-=numberToLeak.toInt

}else{

numDropsInBucket=0

}

timeOfLastDropLeak=now

}

}else{

timeOfLastDropLeak=now

}

if(numDropsInBucket<rate){

numDropsInBucket=numDropsInBucket+1

Page 392: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

returntrue;

}

returnfalse;

}

}

}

Asyoucanseehere,wecreatedaScalaclassthatreceivestwoparameters:rateandperDuration.Rateisaninteger,whichshowshowmanyrequestsweareabletohandlebeforeapplyingbackpressure.PerDurationisaScalaFiniteDuration,whichcanbeanymeasureoftime,suchasmilliseconds,seconds,minutes,orhours.

Thisalgorithmkeepstrackofthetimeforthelastdropinthebucket.Asyoucansee,thecodeissynchronized,butitisfinebecausewewon'tcalleitherexternalresourcesorthreads.

First,wewillgetthecurrenttimewithnewaDate().Thefirsttimewerunthealgorithm,wewillfailontheelsestatement,andwewillgetthecurrenttimeaslastleak.

Thesecondtimeitruns,itwillenteronthefirstIfstatement.Then,wewillcalculatethedelta(diff)betweenthelastleakandnow.ThisdeltawillbedividedbythetimeinmillisecondsthatyoupassedonperDuration.Ifthedeltaisgreaterthan0,thenweleak;otherwisewedrop.Then,wewillcapturethetimeagainforthelastleak.

Finally,wewillcheckthedroprate.Iftherateissmaller,wewillincrementandreturntrue,whichmeanstherequestcanproceed;otherwise,wewillreturnfalse,andtherequestshouldnotproceed.

NowthatwehavethisalgorithmcodedinScala,wecancallforoneofourcontrollers.WewilladdthisbackpressureontheimageRESTAPI.

YourRestApiController.scalashouldlooksomethinglikethis:

packagecontrollers

classRestAPIController@Inject()

(valproductService:IProductService,

valreviewService:IReviewService,

valimageService:IImageService)extendsController{

import

play.api.libs.concurrent.Execution.Implicits.defaultContext

//RESToftheController...

importscala.concurrent.duration._

varbucket=newLeakyBucket(5,60seconds)

defprocessImages={

valfuture=imageService.findAll()

valimages=Awaits.get(5,future)

valjson=ImagesJson.toJson(images)

json

}

defprocessFailure={

Json.toJson("TooManyRequests-TryAgainlater...")

}

Page 393: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

deflistAllImages=Action{

bucket.dropToBucket()match{

casetrue=>Ok(processImages)

casefalse=>

InternalServerError(processFailure.toString())

}

}

}

Here,wewillcreatealeakybucketwithfiverequestsperminute.Wehavetwofunctions:OnetoprocessimagesthatwillcalltheserviceandconverttheobjectstoJSON,andtheothertoprocessfailures.TheprocessFailuremethodwilljustsendamessagesayingthattherearetoomanyrequests,andwecan'tacceptrequestsrightnow.

So,forthelistAllImagesfunction,wewilljustcallthebuckettryingtodropandusetheScalapatternmatcherinordertoprocesstheproperresponse.Iftheresponseistrue,wewillreturnJSONwitha200HTTPcode.Otherwise,wewillreturna500internalerroranddenythatrequest.Here,weimplementedaglobalRATElimiter;however,mostofthetime,peopleperformthisoperationperuser.Now,let'sopenthewebbrowserandtrymorethanfiverequestswithinaminute.Youshouldseesomethinglikethefollowingscreenshotathttp://localhost:9000/REST/api/images/all:

Alright,itworks!Ifyouwaitaminuteandmakerequestsagain,youwillseethattheflowgetsbacktonormal.Thenextstepistoaddanewclienttest,becauseweknowthatifwecallanimagetoomuchinourRESTAPI,wewillbethrottled.

Wewillneedtoaddonemoretestintherest-clientScalaproject.Forthat,wewillneedtochangetheImageProxyTestSpec.

Testingbackpressure

Page 394: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

YourImageProxyTestSpec.scalashouldlooksomethinglikethis:

packageproxy.test

classImageProxyTestSpecextendsFlatSpecwithMatchers{

//RESTofthetests...

"AImageRESTProxy"should"sufferbackpressure"in{

vallatch=newCountDownLatch(10)

varerrorCount:Int=0

for(i<-1to10){

Future{

try{

valimages=ImageProxy.listAll().get

imagesshouldNot(be(null))

for(i<-images){

println(i)

}

}catch{

caset:Throwable=>errorCount+=1

}

latch.countDown()

}

}

while(latch.getCount>=1)

latch.await()

errorCountshouldbe>=5

}

}

So,forthistest,wewillcallImageProxytentimes.Weknowthatnotallrequestswillbeservedaswehavebackpressureontheserver.Here,wecancalltheproxywithatry...catchblockandhaveanerrorcounter.Eachtimeitfails,wecanincrementit.So,here,weareexpectedtofailatleastfivetimes.

WearecreatingthecodewithFeaturesbecausewewanttherequeststohappenatthesametime.WewillneedtouseCountDownLatchfunction,whichisaJavautilityclassthatletsuswaitforallFuturestofinishbeforemovingon.ThisisdonebythecountDownfunction.Everytimeweexecutecountdown,wedecrementtheinternalcounter.Asyoucansee,wecreatedtheCountDownLatchfunctionwithten.

Finally,wehaveawhileblocktowaitforuntilthecounterhaspendingFutures.Nowwewait.Onceit'salldone,wecanchecktheerrorcount;itshouldbeatleastfive.That'sit.Wehavetestedourbackpressuremechanismanditallworks!

Now,itistimetomovetothenextfeaturethatwewillimplementinourapplication:Swagger--wewilladdSwaggersupporttoourRESTAPI.

Page 395: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AddingSwaggersupportSwagger(http://swagger.io/)isasimpleJSONandUIrepresentationtoolforRESTAPIs.Itcangeneratecodeinseverallanguages.Italsocreatesaverynicedocumentation,whichisalsoarunnableSwaggercodethatallowsyoutocallRESTwebservicesfromthedocumentationitgenerates.WewillneedtomakesomechangesinourPlayframeworkapplicationinordertogetSwaggerupandrunningwiththePlayframework.

Firstofall,wewillneedtoaddtheSwaggerdependencytobuild.sbt:

//Restofbuildfile...

libraryDependencies++=Seq(

//Restofotherdeps...

"io.swagger"%%"swagger-play2"%"1.5.2-SNAPSHOT"

)

Asyoucansee,weareusingaSnapshotversion.WhyuseSnapshot?Rightnow,itisnotsupportedonastableversion.Inordertoresolvethisdependency,wewillneedtouseGitandcloneanotherproject.Youcangetmoredetailsathttps://github.com/CreditCardsCom/swagger-play.Basically,youwillneedtowriteacommandasfollows:

$gitclonehttps://github.com/CreditCardsCom/swagger-play.git

$cdswagger-play/

$sbtpublishLocal

Now,wewillneedtoenableSwaggeronReactiveWebStore/conf/application.conf.

Yourapplication.conffileshouldlooksomethinglikethis:

play.modules{

enabled+="play.modules.swagger.SwaggerModule"

}

Next,wecanchangeourcontrollerinordertoaddSwaggersupport.SwaggerhasannotationinordertomaptheRESToperations.

YourRestAPIController.scalafileshouldlooksomethinglikethis:

packagecontrollers

@Singleton

@Api(value="/REST/api",description="RESToperationson

Products,ImagesandReviews.")

classRestAPIController@Inject()

(valproductService:IProductService,

valreviewService:IReviewService,

valimageService:IImageService)extendsController{

import

play.api.libs.concurrent.Execution.Implicits.defaultContext

@ApiOperation(

nickname="listAllProducts",

Page 396: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

value="FindAllProducts",

notes="ReturnsallProducts",

response=classOf[models.Product],

httpMethod="GET"

)

@ApiResponses(Array(

newApiResponse(code=500,message="InternalServer

Error"),

newApiResponse(code=200,message="JSONresponsewith

data")

)

)

deflistAllProducts=Action{

valfuture=productService.findAll()

valproducts=Awaits.get(5,future)

valjson=ProductsJson.toJson(products)

Ok(json)

}

@ApiOperation(

nickname="listAllReviews",

value="FindAllReviews",

notes="ReturnsallReviews",

response=classOf[models.Review],

httpMethod="GET"

)

@ApiResponses(Array(

newApiResponse(code=500,message="InternalServerError"),

newApiResponse(code=200,message="JSONresponsewith

data")

)

)

deflistAllReviews=Action{

valfuture=reviewService.findAll()

valreviews=Awaits.get(5,future)

valjson=ReviewsJson.toJson(reviews)

Ok(json)

}

importscala.concurrent.duration._

varbucket=newLeakyBucket(5,60seconds)

defprocessImages={

valfuture=imageService.findAll()

valimages=Awaits.get(5,future)

valjson=ImagesJson.toJson(images)

json

}

defprocessFailure={

Json.toJson("TooManyRequests-TryAgainlater...")

}

@ApiOperation(

nickname="listAllImages",

value="FindAllImages",

notes="ReturnsallImages-Thereisthrottlingof5

reqs/sec",

response=classOf[models.Image],

httpMethod="GET"

)

Page 397: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

@ApiResponses(Array(

newApiResponse(code=500,message="InternalServerError"),

newApiResponse(code=200,message="JSONresponsewith

data")

)

)

deflistAllImages=Action{

bucket.dropToBucket()match{

casetrue=>Ok(processImages)

casefalse=>InternalServerError(processFailure.toString())

}

}

}

Herewehaveseveralannotations.Firstofall,[email protected],wewilldefinetherootpathoftheRESTAPI.Then,foreachRESTAPIoperation,wehavethe@[email protected]@ApiOperationdefinestheRESTAPIitselfwhereyoucandefinetheparametersandtheHTTPverb,andalsoputsomenotes(documentation).It'salsopossibletodescribetheresult;inourcase,itwillbeaJSONrepresentationofthemodels.

That'sit!WehavethecontrollermappedtoSwagger.ThenextstepistoaddarouteforSwagger.Thisneedstobedonebyaddinganewlineasshowninthefollowingpieceofcodewhich,islocatedatReactiveWebStore/conf/routes:

//RESToftheotherroutes..

GET/swagger.jsoncontrollers.ApiHelpController.getResources

Page 398: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SwaggerUISwaggerwillgenerateaJSONresponseforourRESTAPI.It'spossibletousetheSwaggerUI,whichisveryniceandgiveslotsoffacilitiestothedeveloper.TherearetwowayswecanworkwiththeSwaggerUI:WecanuseitasastandaloneorwecanembedtheSwaggerUIintoourPlayframeworkapplication.

ThestrategywewillpickhereisembeddingtheSwaggerUIinourapplication.IfyouhavemultipleRESTAPIswithmultiplePlayapplicationsormicroservices,itisagoodideatohavethestandaloneinstallationoftheswaggerUI.

Intheprevioussteps,weenabledSwaggerinourapplication.Openyourbrowserandtypehttp://localhost:9000/swagger.json.Youcanfollowtheinstructionsathttp://swagger.io/swagger-ui/.Insummary,youwillgetthefollowingoutput:

Page 399: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

BuildandinstallSwaggerStandalone

Nowwewilldownload,build,andinstallSwaggerStandalone.Let'sgetstartedbywritingthefollowinglinesofcode:

$sudoapt-getupdate$sudoapt-getinstallnodej$sudoapt-getinstallnpm

$gitclonehttps://github.com/swagger-api/swagger-ui.git$cdswagger-ui/$

sudo-Enpminstall-g$sudo-Enpmrunbuild$npmrunserve$GOTO:

http://localhost:8080/

OnceyoustarttheSwaggerUI,youcangotothebrowser,whereyouwillseethefollowing

Page 400: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

output:

Now,let'sembedtheSwaggerUIintoourPlayframeworkapplication.

Wewillneedtocopythecontentfrom/swagger-ui/dist/intoourPlayframeworkapplicationunderReactiveWebStore/public.Then,wewillcreateafoldercalledswaggerui.

WewillneedtoeditonefileinordertoputourswaggerJSONURI.OpenReactiveWebStore/public/swaggerui/index.htmlandchangethe40tothefollowingcodeline:

url="http://localhost:9000/swagger.json";

That'sit.NowwewillneedtocreatealinkfromourapplicationtoembedtheswaggerUI.So,let'schangeReactiveWebStore/app/views/index.scala.html.

Yourindex.scala.htmlfileshouldlooksomethinglikethis:

@(message:String)(implicitflash:Flash)

@main("WelcometoReactiveWebStore"){

<divclass="row-fluid">

<BR>

<divclass="span12">

<divclass="row-fluid">

<divclass="span6">

<ahref="/product"><imgheight="42"width="42"

src="@routes.Assets.at("images/product.png")">Manage

Products</a><BR>

<ahref="/review"><imgheight="42"width="42"

src="@routes.Assets.at("images/review.png")">Manage

Page 401: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Reviews</a><BR>

<ahref="/image"><imgheight="42"width="42"

src="@routes.Assets.at("images/image.png")">Manage

Images</a><BR>

</div>

<divclass="span6">

<ahref="/reports"><imgheight="42"width="42"

src="@routes.Assets.at("images/reports.png")">Reports</a>

<BR>

<ahref="/chat/index_socket"><imgheight="42"width="42"

src="@routes.Assets.at("images/chat.png")">ChatRoom</a>

<BR>

<ahref="/assets/swaggerui/index.html"><imgheight="42"

width="42"

src="@routes.Assets.at("images/swagger.png")">SwaggerREST

API</a><BR>

</div>

</div>

</div>

</div>}

NowwecanrunourPlayapplicationwith$activatorrun.

Openthebrowserandgotohttp://localhost:9000/.Youwillseethefollowingscreenshot:

NowwecanopentheSwaggerUIbyclickingontheSwaggerRESTAPIlinkorbyjustgoingtohttp://localhost:9000/assets/swaggerui/index.html.Itshouldlooksomethinglikethis:

Page 402: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Asyoucansee,theSwaggerUIisverynice.Youcanclickoneachoperationandseemoredetailsonhowtheywork,whichHTTPverbtheyuse,andwhattheURIis.ThereisaTryitout!button.Let'sclickontheTryitout!buttonforproducts,whichwouldlooksomethinglikethis:

Page 403: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Asyoucansee,wehaveourJSONresultandalsosomeCURLsamplesaswell.

Page 404: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtodesignaRESTAPIandchangedyourPlayframeworkapplicationinordertohaveSwaggersupport.YoucreatedaScalaclientlibraryusingproxytechniques,aswellasScalatestsfortheAPIs.Additionally,youwereintroducedtobackpressureusingtheleakybucketalgorithm.

Inthenextchapter,whichwillbethefinalchapter,youwilllearnaboutsoftwarearchitectureandscalability/resiliencytechniques,suchasdiscoverability,loadbalancers,caches,AkkaCluster,andtheAmazonCloud.

Page 405: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Chapter10.ScalingupInthepreviouschapters,webuiltaScalaandPlayframeworkapplication.WeusedthemosteffectiveframeworksandtoolsaroundtheScalaecosystem,suchasPlayframeworkandAkka;andweusedtheReactiveandFunctionalProgrammingtechniquesusingFuturesandRxScala.Additionally,wecreatedreportswithJasperandChatwithWebSockets.Thisisthefinalchapter,andwewilllearnhowtodeployandscaleourapplication.

Inthischapter,wewillcoverthefollowingtopics:

StandalonedeployArchitectureprinciplesReactivedriversanddiscoverabilityMid-Tierload-balancer,timeouts,Backpressure,andcachingScalingupmicroserviceswithanAkkaclusterScalinguptheinfrastructurewithDockerandAWScloud

Page 406: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

StandalonedeployThroughoutthisbook,weusedtheActivatorandSBTbuildanddevelopmenttools.However,whenwetalkaboutproduction,wecan'truntheapplicationwithSBTorActivator;weneedtodoastandalonedeploy.

WhataboutstandardJavaServletcontainers,suchasTomcat?Tomcatisgreat;however,Playisgreater.Youwon'tgetbetterperformancebydeployingyourapponTomcat.ThestandaloneplayusesNetty,whichhassuperiornetworkstack.

TherearesomesmallchangesthatwewillneedtomakeinordertodeployourapplicationforJasperreports.Don'tworry;thesechangesareverysimpleandstraightforward.

Page 407: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReportsfolderWeneedtomovethereportstemplate(JRXMLfiles)fromthesourcefoldertothepublicfolder.Whydoweneedtodothis?Becausewhenwegeneratethestandalonedeploy,theywon'tbeincludedintheapplicationjars.WhatisinsidethepublicfolderwillbepackedanddeployedintoaproperJARfile.That'swhyweneedtomakethischange.

CreateafoldercalledreportsatReactiveWebStore/public/.ThenmovealltheJRXMLfilesthere.

Page 408: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ChangingreportbuilderAsourtemplateswillbeinsideaJARfile,weneedtochangetheloadinglogicinordertogetthetemplatesproperly.UnderReactiveWebStore/app/report/,weneedtochangeReportBuilder.scala,whichshouldlooksomethinglikethisafterediting:

packagereports

objectReportBuilder{

privatevarreportCache:scala.collection.Map[String,Boolean]=

newscala.collection.mutable.HashMap[String,Boolean].empty

defgenerateCompileFileName(jrxml:String):String=

"/tmp/report_"+jrxml+"_.jasper"

defcompile(jrxml:String){

if(reportCache.get(jrxml).getOrElse(true)){

valdesign:JasperDesign=JRXmlLoader.load(

Play.resourceAsStream("/public/reports/"+jrxml)

(Play.current).get)

JasperCompileManager.compileReportToFile(design,

generateCompileFileName(jrxml))

reportCache+=(jrxml->false)

}

}

deftoPdf(jrxml:String):ByteArrayInputStream={

try{

valos:OutputStream=newByteArrayOutputStream()

valreportParams:java.util.Map[String,Object]=

newjava.util.HashMap()

valcon:Connection=

DriverManager.getConnection("jdbc:mysql://localhost/RWS_DB?

user=root&password=&useUnicode=

true&useJDBCCompliantTimezoneShift=

true&useLegacyDatetimeCode=false&serverTimezone=UTC")

compile(jrxml)

valjrprint:JasperPrint=

JasperFillManager.fillReport(generateCompileFileName(jrxml),

reportParams,con)

valexporter:JRPdfExporter=newJRPdfExporter()

exporter.setExporterInput(newSimpleExporterInput(jrprint));

exporter.setExporterOutput(

newSimpleOutputStreamExporterOutput(os));

exporter.exportReport()

newByteArrayInputStream

((os.asInstanceOf[ByteArrayOutputStream]).toByteArray())

}catch{

casee:Exception=>thrownewRuntimeException(e)

null

}

}

}

Themainchangesthatwemadewerearoundthecompilefunction.Now,weareusingtheJasperJRXmlLoaderinordertoloadtheJaspertemplatefromanInputStreammethod.PassingtheInputStreammethodprovidedbythePlay.resourceAsStreamfunction.Asyou

Page 409: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

cansee,wearepassingthenewpath,/public/reports/,inordertogetthetemplates.Therestofthecodeisprettymuchthesameastheoneweexecutedearlier.

Nowwearereadytodeploy.Inordertodoso,wewillneedtorunacommandonactivator,whichisasfollows:

$activatoruniversal:packageZipTarball

Youwillseethefollowingresult:

Assoonasthetaskisfinished,ourappwillbepackedintotheReactiveWebStore/target/universal/directory,andyouwillseeareactivewebstore-1.0-SNAPSHOT.tgzfile.

Thenyouneedtoextractthefileandyoushallhavethefollowingdirectory:

reactivewebstore-1.0-SNAPSHOTbin:Scriptstoruntheappconf:Allconfigfiles:routes,logging,messagesbib:AllJARsincludingthird-partydependenciesshare:Alldocumentationabouttheapp

Page 410: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

DefiningthesecretBeforewerunourstandaloneapplication,wewillneedtoapplyonemorechange.Weneedtochangethedefaultsecret.Locatethereactivewebstore-1.0-SNAPSHOT/conf/application.conffile.

Changethesecrettofollowingintheapplication.conffile:

play.crypto.secret="changeme"

Youwillneedtoprovideadifferentvalue.Itcanbeanything,aslongasyoudon'tcallitchangeme.Ifyoudon'tchangethis,yourapplicationwillnotbootup.Youcangetmoreinformationathttp://www.playframework.com/documentation/latest/ApplicationSecret.

Ifyoujustwanttotestthedeployfornow,let'scallitplayworks.

Now,weareallsettostarttheapplication.

Page 411: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

RunningthestandalonedeployInordertoruntheapp,wewillusethescriptgeneratedbytheuniversaltask.Gotothereactivewebstore-1.0-SNAPSHOTdirectoryandthenrun$bin/reactivewebstore,whichwouldlooksomethinglikethis:

Now,youcanopenyourbrowserandgotohttp://localhost:9000/.

That'sit;wehaveourappupandrunning.Feelfreetotestallthefeatureswebuilt--theyallwork!

Itshouldlooksomethinglikethis:

Page 412: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ArchitectureprinciplesScalabilityisabouthandlingmoreusers,traffic,anddata;andinordertodoit,wewillneedtoapplysomeprinciplesandtechniques.Ourapplicationisalreadyusingthemostmoderntechniquesandtechnologies,suchasfunctionalandReactiveXprogramming,RxScala,Akkaframework,Play,andmuchmore.However,inordertoscale,wewillneedtohaveaninfrastructureinplaceandotherkindsofsystemthatwillallowustohandlemoreusers.

Agoodapplicationarchitectureshouldbecreatedaroundthefollowingprinciples:

SeparationofConcerns(SOC)(moredetailsathttps://en.wikipedia.org/wiki/Separation_of_concerns)ServiceOrientation(SOA/microservices)PerformanceScalability/Resiliency

Let'sseetheseprinciplesindetail.

Page 413: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Serviceorientation(SOA/microservices)Serviceorientationisabouthavingahigherlevelofabstraction,whichisalsocalledservicesormicroservices.SOAisnotaboutaspecifictechnology,butaboutprinciplessuchassharedservices,flexibility,andintrinsicoperability.IfyouwanttolearnmoreaboutSOA,checkouttheSOAManifestoathttp://www.soa-manifesto.org/.MicroservicesisaparticularflavorofSOAwherethemaindifferenceisthefocusonthegranularity,autonomy,andisolation.Ifyouwanttolearnmoreaboutmicroservices,youcancheckouthttps://www.linkedin.com/pulse/soa-microservices-isolation-evolution-diego-pachecoaswellashttp://microservices.io/.

Page 414: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

PerformanceTherightalgorithmmightmakeyourapplicationworksmoothlyandthewrongalgorithmmightmakeyourusershaveapoorexperience.Performanceisachievedbydesign--firstofall,choosetherightsetofcollectionsandtherightsetofalgorithmsandframeworks.However,performanceneedstobemeasuredandtunedeventually.Thepracticeyoushoulddoinyourapplicationwithregardofperformanceisstresstesting.ThebeststresstestingtoolsintheScalaecosystemisGatling.Gatling(http://gatling.io/#/)allowsyoutocodeinScalausingaverysimpleyetpowerfulDSL.GatlingfocusesonHTTPandlatencypercentilesanddistributions,whichistherightthingtodonowadays.Latencyisnotonlyusedforthesakeofperformanceandscalability,butitisalsoheavilyrelatedtouserexperienceaseverythingisonline.

Page 415: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Scalability/ResiliencyScalabilityisoneofthemainreasonswhywedosoftwarearchitecture,becauseanarchitecturethatdoesnotscaledoesnothavebusinessvalue.WewillcontinuetalkingaboutScalabilityprinciplesduringthischapter.Resiliencyisabouthowmuchthesystemcanresistandkeepoperatingunderthemostadversesituations,suchashardwarefailureorinfrastructurefailure.Resiliencyisanoldterm.Currently,thereisanewandmoremodernandaccurateprinciplecalledantifragility.ThisprinciplewaswelldevelopedandusedinpracticebyNetflix.Antifragilityisaboutsystemsandarchitecturethatcanadaptandfailovertoothersystemsandotheroperationalmodestokeepworkingnomatterwhat.Ifyouwanttoknowmoreaboutantifragility,youcanvisithttp://diego-pacheco.blogspot.com.br/2015/09/devops-is-about-anti-fragility-not-only.htmlandhttp://diego-pacheco.blogspot.com.br/2015/11/anti-fragility-requires-chaos.html.

Page 416: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalabilityprinciplesHavingarchitecturearoundtheseprinciplesmakesitpossibletoscaleyourapplicationup.However,wewillstillneedtorelyonotherprinciplesandtechniquestoscaleit.

Thereareseveralprinciplesandtechniquesforscalability,whichareasfollows:

Verticalandhorizontalscaling(upandout)CachingProxyLoadbalancerThrottlingDatabaseclusterCloudcomputing/containersAutoScalingReactivedrivers

Page 417: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Verticalandhorizontalscaling(upandout)Youcanaddmoreresources,havebetterhardware,oryoucanaddmoreboxes.Thesearethetwobasicwaystoscale.Youcanalwaysimproveandtuneyourapptousefewerresourcesandgetmorefromasinglebox.Recently,therewereseveralimprovementsinthisareaaroundreactiveprogrammingthatusesfewerresourcesanddeliversmorethroughput.However,therearealwayslimitstowhichasingleboxcanprovideinsenseofscalingup,whichiswhywealwaysneedtobeabletoscaleout.

Page 418: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

CachingDatabasesaregreat.However,thereisalatencycosttocallatraditionaldatabase.Agreatwaytofixthisishavingamemorycache,whichyoucanuseasasubsetofyourdataandgetthebenefitoffastretrieval.Playframeworkhascachesupport.Ifyouwanttolearnmore,checkouthttps://www.playframework.com/documentation/2.5.x/ScalaCache.

Thereareotheroptionsinsenseofcaching.Therearelotsofcompaniesthatusethememoryasadefinitivedatastorenowadays.Forthis,youcanconsidertoolssuchasRedis(http://redis.io/)andMemcached(https://memcached.org/).However,ifyouwanttoscaleRedisandMemcached,youwillneedsomethinglikeNetflix/Dynomite(https://github.com/Netflix/dynomite).DynomiteprovidesaclusterbasedonAWSDynamopaperforRedis,whichhasthefollowingbenefits:

HighthroughputandlowlatencyMulti-regionsupport(AWScloud)TokenawareConsistenthashingReplicationShardingHighavailability

Note

Ifyouwanttolearnmoreaboutdynomite,checkouthttps://github.com/Netflix/dynomite/wiki.

Page 419: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

LoadbalancerAloadbalancerisakeytooltoscaleservers.So,let'ssay,youhave10boxeswithourPlayframeworkapplicationor10Dockercontainers.Wewillneedsomethinginfrontofourapplicationtodistributethetraffic.Thereareseveralserversthatcandothis,suchasNGINX(https://nginx.org/)andApacheHTTPServer(https://httpd.apache.org/).Ifyouwanttoscaleyourapplication,thisistheeasiestsolutionforit.Configurationandmoredetailscanbefoundathttps://www.playframework.com/documentation/2.5.x/HTTPServer#Setting-up-a-front-end-HTTP-server.

Loadbalancersareoftenproxyserversaswell.YoucanusethemtohaveHTTPSsupport.Ifyouwant,youcanhaveHTTPSonPlayframeworkaswell(https://www.playframework.com/documentation/2.5.x/ConfiguringHttps).KeepinmindthatyouwillneedtochangeswaggerembeddedinstallationasallthecodethatwehavepointstotheHTTPinterface.IfyouaredoingdeploysintheAWScloud,youwillneedtochangesomeoftheconfigurationtoforwardtheproxies,whichyoucanfindathttps://www.playframework.com/documentation/2.5.x/HTTPServer#Setting-up-a-front-end-HTTP-server.

Page 420: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThrottlingThisisalsoknownasBackpressure.WecoveredthrottlinginChapter9,DesignYourRESTAPI.Youcangetmoredetailsthere.However,themainideaistolimittherequestforeachuser.Thisisalsoawaytomakesurethatasingleuserdoesnotstealallcomputationalresources.Thisisalsoimportantfromthesecuritypointofview,especiallyfortheservicesthatareInternet-facingoralsoknownasedge.AnothergreatwaytoprotectandhavethiscapabilityisusingNetflix/Zuul(https://github.com/Netflix/zuul).

Page 421: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

DatabaseclusterSometimes,theproblemisnotontheapplicationside,butinthedatabase.Whenwetalkaboutscalability,weneedbeabletoscaleeverything.WeneedtohavethesameconceptsfordatabasesthatwehavefortheMid-Tier.Fordatabases,itisimportanttoworkwiththefollowing:

ClusteringIndexMaterializedviewsDatapartition

Forourapplication,weusedtheMySQLdatabase.Herearesomeresourcesthatcanhelpyouscalethedatabaseandapplythepreviousconcepts:

http://dev.mysql.com/doc/refman/5.7/en/faqs-mysql-cluster.htmlhttp://www.fromdual.com/mysql-materialized-viewshttp://dev.mysql.com/doc/refman/5.7/en/optimization-indexes.htmlhttp://dev.mysql.com/doc/refman/5.7/en/partitioning.htmlhttps://dev.mysql.com/doc/refman/5.7/en/partitioning-overview.html

Page 422: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Cloudcomputing/containersScalingupapplicationintraditionaldatacentersisalwayshardbecauseweneedtohavethehardwareinplace.Thisgetsdonebythepracticeofcapacityplanning.Capacityplanningisgreattomakesurewedon'tspendmoneybeyondourbudget.However,itisveryhardtogetitdoneright.Softwareishardtopredict,andthat'sagreatadvantageofthecloud.Cloudisjustanotherlevelofabstraction.Hardwareandnetworksbecomelogical,andtheyareencapsulatedbehindAPIs.Thismakesiteasiertoscaleourapplicationaswecanrelyoncloudelasticityandscaleondemandwhenweneedto.However,thearchitectureneedstobereadyforthismomentandusethetoolsandtechniquesdescribedinthischapter.Currently,thereareseveralpublicclouds;thebestoptionsareasfollows:

AWS--AmazonCloud(https://aws.amazon.com/)GoogleCloud(https://cloud.google.com/)MicrosoftAzureCloud(https://azure.microsoft.com/en-us/)

Today,Cloudisnottheonlybigelephantintheroom.WealsohavetheLinuxcontainers,suchasDocker(https://www.docker.com/)andLXC(https://linuxcontainers.org/).Containersprovideanotherlevelofabstraction,andtheycanrunonthecloudoronpremises.Thismakesyourapplicationmoreportableandalsomorecosteffective.Containersalsoscale.Themainadvantagearoundcontainersisspeedandflexibility.It'swayfastertobootupacontainerincomparisonwithavirtualizedimageinanypubliccloud.Theyarealsoportableandcanruneverywhere.

Page 423: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AutoScalingCurrently,thisisoneofthegreatestresourcesofcloudcomputing.Basically,youcandefineabaseimage,whichisastateofanoperationalsystemsuchasLinux,andthecloudwillcreateanddestroyinstancesforyouondemand.Theseinstancescanbecreatedbytheincreaseincomputationalresources,suchasmemory,CPU,network,orevenbasedoncustomrules.Thisisthekeyconcerninordertohaveelasticity.IfyouwanttolearnmoreaboutAutoScaling,checkouthttps://aws.amazon.com/autoscaling/.

Page 424: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

AnoteaboutautomationInordertouseallthesetechniquesandtechnologiesatscale,weneedtohavefullautomation(https://en.wikipedia.org/wiki/List_of_build_automation_software)becauseitisimpossibletohandleallthiswithmanualwork.Whenweareusingthecloudorcontainers,thereisnootherwayaround;everythingneedstobeautomated.Thereareseveraltoolsthathelpusachievethisgoal,suchasAnsible(https://www.ansible.com/).

Page 425: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Don'tforgetabouttelemetryWhenyouhaveallinfrastructuresinplace,youwillalsoneedtohavemonitoring,alerting,andproperdashboards.Thereareplentyofgreattoolsforcontainersandpublicclouds,suchasSensu(https://sensuapp.org/)andPrometheus(https://prometheus.io/).

Page 426: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ReactiveDriversanddiscoverabilityReactiveDrivers:WetalkedalotanddidalotofreactivecodeusingPlayframeworkandRxScala.However,tohavefullbenefitsofReactiveXprogramming,youneedtomakesureeverythingisanon-blockingIOandreactive.Inotherwords,weneedtohaveallofourdriversreactive.SlickisgreatbecauseitgivesusreactivitywiththeMySQLdatabase.Wewillneedtoapplythesameprincipleseverywherewehaveadriverorconnectionpoint.Therearelotsoflibrariesbecomingreactivethesedays.Forinstance,ifyouwanttocacheusingRedis,youcanuseLettuce(https://github.com/mp911de/lettuce),whichisreactive.

Whenweworkwithmicroservices,wetendtohavehundredsofmicroserviceinstances.Thesemicroserviceswillrunoncontainersand/orcloudcomputingunits.Youcan'tpointtospecificIPsbecausethecodewillnotbemanagedandwillnotsurviveinacloud/containerenvironment.Cloud/containerinfrastructureisephemeral,andyoudon'tknowwhenaninstancewillbeterminated.That'swhyyouneedtobeabletoswitchtoanotheravailabilityzoneorregionatanymoment.

Therearetoolsthatcanhelpusapplythesechangesinourcode.ThesetoolsareNetflix/Eureka(https://github.com/Netflix/eureka)andConsul(https://www.consul.io/),orevenApacheZookeeper(https://zookeeper.apache.org/).Eurekahasoneadvantage--itiseasiertouseandhastoolsaroundtheJVMecosystem,whichwasbattletestedbyNetflix.

EurekaisacentralregistrywheremicroservicesregistertheirIPandmetadata.EurekahasaRESTAPI.MicroservicescanusetheEurekaAPItoqueryandsearchexistingapplications.Eurekacanruninamulti-vpc/multi-regionenvironment.ThereareotherJVMcomponents,suchasribbon(https://github.com/Netflix/ribbon)andkaryon(https://github.com/Netflix/karyon),whichcanautomaticallyregisterandretrieveeurekainformationandmetadata.

BasedontheEurekainformation,youcanperformmicroserviceloadbalancingandfailovertootheravailabilityzonesandregionsautomatically.WhyuseEurekaifIcanuseDNS?DNSforMid-TierloadbalancingisnottherightchoiceasDNSisnotflexibleandthetimeoutisquitebig.Ifyouwantknowmoreaboutdiscoverability,checkouthttp://microservices.io/patterns/service-registry.html.

Page 427: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Eurekaoverview-EurekaarchitectureoverviewontheAWScloud

Asyoucanseeintheprecedingdiagram,youwilldeploytheEurekaserveratleastinthreeAvailabilityZones(AZs)inordertohaveavailability.Then,Eurekadatawillbereplicatedtoeachserver.OurapplicationsormicroserviceswillregisterinEureka,andotherapplications/microservicescanretrievethismetadata,suchasIPaddress,tothemtotheRESTcalls.Ifyouwantlearnmore,youcancheckouthttps://github.com/Netflix/eureka/wiki/Eureka-at-a-glance.

Page 428: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Mid-Tierloadbalancer,timeouts,Backpressure,andcachingEureka,Zookeeper,orConsulareonlyonepartoftheequation.WestillneedsomesoftwareontheclientsidethatcanusetheEurekainformationinordertodoMid-Tierloadbalancing,failover,andcaching.TheNetflixstackhasacomponentforthat,whichiscalledribbon(https://github.com/Netflix/ribbon).Withribbon,youcanautomaticallyresolvethemicroserviceIPsfromEureka,doretries,andfailovertootherAZsandregions.Ribbonhasacacheconcept;however,itisonpreloadedcache.

Ribbonideasaresimple.Thegreatthingaboutribbonisthateverythingisreactive,andyoucanuseRxJavaandRxScalainordertoworkwiththestack.Ifyoudon'twanttouseribbon,youcanstillcreateasimpleintegrationlayerwithScalaandperformthesameconcerns,suchasloadbalancing,failover,andcaching.

WhataboutBackpressure?BackpressurecanbedonewithRxJavaandRxscala,andyouwillbeabletodoitontheclientsideaswell.YoucanlearnmoreaboutBackpressureinRxathttps://github.com/ReactiveX/RxJava/wiki/Backpressure.

So,ifIhaveclient-sideloadbalancing,failover,caching,andBackpressure,amIgoodtogo?Yes,youare;however,wecanalwaysdobetter.Workingwithmicroservicesisnoteasyaseverythingisaremotecall,andremotecallscanfail,hang,ortimeout.Theseconsarehardanddangerousifnotmanagedwell.Thereisanothersolutionthatcanhelpusalotwiththisconcept;itiscalledHystrix(https://github.com/Netflix/Hystrix).

HystrixisalibraryfortheJVMdesignedforlatencyandfaulttoleranceprotection.Ataglance,Hystrixisawrapperaroundanyremotecodethatcantaketimeorgowrong.

Hystrixhasthreadisolationandprovidesadedicatedthreadpoolforeachresource.Thisisgreatbecauseitpreventsyoufromrunningoutofresources.Ithasanexecutionpatterncalledcircuitbreaker.Circuitbreakerwillpreventrequestsfromtearingdownthewholesystem.Additionally,ithasadashboardwherewecanvisualizethecircuits,so,atruntime,wecanseewhat'sgoingon.Thiscapabilityisgreatnotonlyforsenseoftelemetry,butalsobecauseitiseasytotroubleshootandvisualizewheretheproblemis.

Itcanbefurtherexplainedwiththehelpofthefollowingflowchart:

Page 429: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ThecodeyouwanttoprotectwillbearoundaHystrixcommand.Thiscommandcanbemanipulatedinsyncorasyncprogrammingmodels.ThefirstthingthatHystrixwillcheckisifthecircuitisclosed,whichisgood,andhowitshouldbe.Then,itchecksiftherearethreadsavailableforthatcommand,andifthereareavailablethreads,thenthecommandwillbeexecuted.Ifthisfails,ittriestogetafallbackcode,whichisasecondoptionthatyoucanprovideincaseoffailure.Thisfallbackshouldbestatic;however,youcanbeloadingdatainthebackgroundandthenreturnonthefallback.AnotheroptionisfallbacktootherAZorRegion.

FollowingisasnapshotofhowaHystrixdashboardcircuitbreakerviewwouldwork:

Page 430: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

Intheprecedingimage,wecanseetheHystrixdashboardsample,wherewecanvisualizecriticalinformation,suchassuccessanderrorrateandifthecircuitisopenorclosed.IfyouwantlearnmoreabouttheHystrixdashboard,checkouthttps://github.com/Netflix/Hystrix/wiki/Dashboard.

Page 431: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalingupmicroserviceswithanAkkaclusterOurapplicationalsousesAkka.InordertoscaleAkka,wewillneedtouseanAkkacluster.TheAkkaclusterallowsustoclusterizeseveralActorsystemsinseveralmachines.IthasspecialActorroutersthatareclusteraware,andwecanusetheseActorstorouterequeststothewholecluster;moredetailscanbefoundathttp://doc.akka.io/docs/akka/2.4.9/java/cluster-usage.html#Cluster_Aware_Routers.

TheAkkaclusterprovidesmembershipprotocolandlifecycle.Basically,wecanbenotifiedbytheclusterwhenanewmemberjoinsorwhenamemberleavesthecluster.Giventhiscapability,itispossibleforustocodeascalablesolutionaroundthesesemantics.Asweknowwhenamemberjoins,wecandeploymorenodes,andwecanalsodropnodesondemand.

AsimplesamplewouldbetocreateanActorcalledfrontend,andwhenweseethisActor,wecoulddeploythreebackendActorsacrossthecluster.IfthefrontendActorleaves,wecouldundeploytheotherActors.AllthislogiccanbearchivedusingthemembershipprotocolandclusterseventsthatAkkageneratesforus.AfrontendActorisanotaUIorwebapplication,itisjustanActorthatreceiveswork.So,let'ssaywewanttogenerateanalyticsaroundourproductscatalog.WecouldhaveafrontendActorwhoreceivesthatrequestanddelegatestheworktobackendActors,whichwillbedeployedacrosstheclusteranddelivertheanalyticalwork.

ThefollowingimageistheprocessviewofanAkkaclustermembershipprotocol:

Asyoucanseeintheprecedingimage,thereisasetofstates.Firstofall,thenodeisjoining

Page 432: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

thecluster;thenthenodecanbeup.Oncethenodeisup,itcanleavethecluster.Thereareintermediatestates,suchasleavingandexisting.

TheAkkaclusterprovidesmanyoptionstoscaleourActorsystem.AnotherinterestingoptionistousethepatternofdistributedPub/Sub.IfyouarefamiliarwithJMSTopics,itisalmostthesameidea.Forthosewhoarenotfamiliar,youcancheckouthttp://doc.akka.io/docs/akka/2.4.9/java/distributed-pub-sub.html.

Note

IfyouwantlearnmoreabouttheAkkacluster,youcancheckouthttp://doc.akka.io/docs/akka/2.4.9/common/cluster.html.

Page 433: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

ScalinguptheinfrastructurewithDockerandAWScloudScalingupwiththeAWScloudiseasy,asatanymoment,withasimpleclickontheAWSconsole,youcanchangethehardwareandusemorememory,CPU,orbetternetwork.Scale-outisnothard;however,weneedtohavegoodautomationinplace.ThekeyprincipletoscaleistohavetheAutoScalinggroupsinplacewithgoodpolicies.Youcanlearnmoreaboutitathttp://docs.aws.amazon.com/autoscaling/latest/userguide/policy_creating.html.

Thereareotherinterestingservicesandcomponentsthatcanhelpyouscaleyourapplication.However,youwillneedtokeepinmindthatthiscanleadtocoupling.TheITindustryismovingtowardthecontainerdirectionbecauseitisfaster,andit'seasytodeployinotherpublicclouds.

WecanscaleoutwithDockeraswell,becausethereareclustermanagersthatcanhelpusscaleourcontainers.Currently,thereareseveralsolutions.Inthesenseofcapabilitiesandmaturity,thefollowingarethebettersolutions:

DockerSwarm(https://docs.docker.com/swarm/overview/)Kubernetes(http://kubernetes.io/)ApacheMesos(http://mesos.apache.org/)

DockerSwarm:ThisisaclusterforDocker.DockerSwarmisveryflexibleandintegrateswellwithotherDockerecosystemtools,suchasDockermachine,Dockercompose,andConsul.Itcanhandlehundredsofnodes,andyoucanlearnmoreaboutthemathttps://blog.docker.com/2015/11/scale-testing-docker-swarm-30000-containers/.

Kubernetes:ThiswascreatedbyGoogle,anditisafullsolutionfordevelopmentautomation,operation,andscalingDockercontainers.TheKubernetesclusterhastworoles,amasternodethatcoordinatesthecluster,schedulesapplications,andkeepsapplicationsonadesiredstate;andtherearenodes,thatareworkersthatrunapplications.Itcanhandlehundredsofcontainersandscaleverywell.Tolearnmoreaboutit,checkouthttp://blog.kubernetes.io/2016/03/1000-nodes-and-beyond-updates-to-Kubernetes-performance-and-scalability-in-12.html.

ApacheMesos:ThiswascreatedbyTwitter.Itisveryinteresting,asyoucanrunabaremetalonapremisesdatacenteroronapubliccloud.MesosallowsyoutouseDockercontainersaswell.Ifyouwanttolearnmoreaboutmesos,checkoutthefollowingpaper:

http://mesos.berkeley.edu/mesos_tech_report.pdf

Page 434: Building Applications with Scala - Sergey Drozdov. Sausage ...sd.blackball.lv/library/Building_Applications_with_Scala_(2016).pdf · Building Applications with Scala Credits About

SummaryInthischapter,youlearnedhowtodeployyourPlayframeworkapplicationasastandalonedistribution.Additionally,youlearnedseveralarchitecturalprinciples,techniques,andtools,tohelpyouscaleoutyourapplicationtothousandsofusers.

Withthis,wealsoreachtheendofthisbook.Ihopeyouenjoyedthisjourney.WebuiltaniceapplicationusingScala,PlayFramework,Slick,REST,Akka,Jasper,andRxScala.Thankyouforyourtime.IwishyouthebestinyourcodingcareerwiththeScalalanguage.