258 62 8MB
English Pages 736 Year 2007
RICH INTERNET APPLICATIONS WITH
ADOBE
®
FLEX & JAVA ™
™
FREE DVD
$695 VALUE
Written by
Yakov Fain Dr.Victor Rasputnis AnatoleTartakovsky Guest chapter by www.theriabook.com The World’s Leading i-Technology Publisher
Ben Stucki
Forewords by Bruce Eckel & Matt Chotini RIAWITHADOBEFLEXANDJAVA
ii
RIAWITHADOBEFLEXANDJAVA
RICHINTERNETAPPLICATIONS
ADOBE
®
WITH
FLEX&JAVA TM
TM
SECRETSOFTHEMASTERS FirstEdition
WRITTENBYYAKOVFAIN,DR.VICTORRASPUTNIS&ANATOLETARTAKOVSKY
SYS-CONMedia WoodcliffLake,NJ07677
RIAWITHADOBEFLEXANDJAVA
iii
RichInternetApplicationswithAdobe®Flex™&Java™ SecretsoftheMasters 1stEdition SYS-CONBooks/2007 Allrightsreserved. Copyright©2007bySYS-CONMedia ManagingEditor:NancyValentine CoverDesign:AbrahamAddo LayoutandDesign:AbrahamAddo ProducedbySYS-CONMedia Thisbookmaynotbereproducedinwholeorinpartinanyformorbyanymeans, electronicormechanicalincludingphotocopyoranyinformationstorageor retrievalsystem,withoutwrittenpermission.Forfurtherinformation aboutSYS-CONBooksandmagazineswrite: SYS-CONMedia,577ChestnutRidgeRoad,WoodcliffLake,NJ07677. AdobeandAdobeproductsareeitherregisteredtrademarkortrademarksofAdobe SystemsIncorporatedintheUnitedStatesand/orothercountries. JavaandJava-basedmarksaretrademarksorregisteredtrademarksofSun Microsystems,Inc.,intheUnitedStatesandothercountries Throughoutthisbook,tradenamesandtrademarksofsomecompaniesandproducts havebeenused,andnosuchusesareintendedtoconveyendorsementoforother affiliationswiththebook. SYS-CON,theeditors,andtheauthorsofthisbookspecificallydisclaimallother warranties,expressorimplied,includingbutnotlimitedtoimpliedwarrantiesof merchantabilityandfitnessforaparticularpurposewithrespecttothesamplecode, applicationsorprograms,theprogramlistingsinthebook,and/orthetechniques describedinthebook,andinnoeventshallSYS-CON,theeditorsand/ortheauthors beliableforanylossofprofitoranyothercommercialdamage,includingbutnot limitedtospecial,incidental,consequential,orotherdamages.
ISBN0-9777622-2-X PublishedandprintedintheUnitedStatesofAmerica 098765432
iv
RIAWITHADOBEFLEXANDJAVA
RIAWITHADOBEFLEXANDJAVA
v
vi
RIAWITHADOBEFLEXANDJAVA
ABOUTTHEAUTHORS
AbouttheAuthors Yakov Fain is a Managing Principal of Farata Systems. He’s responsible for the enterprise architecture and emerging technologies.Yakov has authored several Java books, dozens of technical articles,andhisblogishugelypopular.SunMicrosystemshasawardedYakovwiththetitleJava Champion.HeleadsthePrincetonJavaUsersGroup.YakovholdsaBSandanMSinAppliedMath andisanAdobeCertifiedFlexInstructor.Youcanreachhimat[email protected]. Dr.VictorRasputnisisaManagingPrincipalofFarataSystems.He’sresponsibleforprovidingarchitecturaldesign,implementationmanagement,andmentoringtocompaniesmigratingtoXML Internettechnologies.HeholdsaPhDincomputersciencefromtheMoscowInstituteofRobotics. Youcanreachhimat[email protected]. AnatoleTartakovskyisaManagingPrincipalofFarataSystems.He’sresponsibleforthecreationof frameworksandreusablecomponents.Anatolehasauthoredanumberofbooksandarticleson AJAX,XML,theInternet,andclient/servertechnologies.HeholdsanMSinmathematics.Youcan reachhimat[email protected]. BenStuckiisasoftwareengineeratAtellis,aWashington,D.C.,basedfirmthatspecializesindevelopingrichInternetapplicationsandproducts.AspartofthecoredevelopmentteamatAtellis, Benmixeshisknowledgeofback-endapplicationdevelopmentandinteractiveuserinterfacesto engineernext-generationFlexapplicationsandcomponents.Benisalsoanactivememberofthe FlashandFlexcommunitiesandmanagesanumberofopensourceprojects. Theauthorsfrequentlyblogatthefollowinglocations: • FlexBlog:http://flexblog.faratasystems.com/ • YakovFain’sblog:http://yakovfain.javadevelopersjournal.com/ • BenStucki’sblog:http://blog.benstucki.net/
RIAWITHADOBEFLEXANDJAVA
vii
viii
RIAWITHADOBEFLEXANDJAVA
TABLEOFCONTENTS
CONTENTS ACKNOWLEDGMENTS FOREWORDS
xix xxi
CHAPTER1
1 1 4 4 6 7 7 8 10 10 10 11 11 11 12 13 13
ArchitectureofRichInternetApplications RIAPlatforms:TheMajorChoices AdobeFlex2 Java WPF AJAX AJAXShortcomings OtherRIASolutions OpenLaszlo GWT Nexaweb Canoo Backbase Apollo,Desktop2.0,andtheBrightFuture SomePragmaticFlex/JavaConsiderations TheLearningCurve:ReuseofSkillsandCode
RIAWITHADOBEFLEXANDJAVA
ix
TABLEOFCONTENTS ApplicationSecurity FlexGUIPerformance ShorterDevelopmentCycle RoomforImprovement ConnectionManagement FlexandAgileDevelopment Summary CHAPTER2
GettingFamiliarwithFlex FreeFlex2Ingredients DownloadingtheFlex2Framework HelloWorldinMXML SpecifyingCompilerOptions BuildingandDeployingApplicationswithAnt BuildingHelloWorldwithAnt FrameRate NamespacesinMXML FromHelloWorldtoaCalculator AddingSomeActionScript TheFirstDatewithEvents WhyActionScript3.0? ComparingActionScript3.0andJava5 FlexFrameworkAPIDocumentation SeparatingDesignandDevelopment WorkingwithDisplayObjectsinActionScript TheApplicationLoadingProcess Summary Endnotes CHAPTER3
FlexBuilderDevelopmentEnvironment InstallingandConfiguringFlexBuilder YetAnotherHelloWorld WorkingwiththeSourcePanel RunningHelloWorld BuildingtheProject FlexBuilder-GeneratedFiles RunningApplicationsinFlexBuilder SimpleEventProcessing
x
RIAWITHADOBEFLEXANDJAVA
13 14 14 14 14 15 16 19 19 20 21 22 24 25 25 27 29 30 33 34 36 37 42 43 45 45 47 47 49 49 50 50 55 56 57 57 58 59
TABLEOFCONTENTS APureMXMLVersion SpecifyingEventsHandlers Layouts ViewStatesandTransitions FineTuningFlexBuilder DebuggingwithFlexBuilder ProjectTypesandDataAccess GeneratedActionScript FlexBuilderTipsandKeyboardShortcuts UsingtheSubversionVersionControlSystem Summary CHAPTER4
LearningFlexThroughApplications ActionScriptDynamicClasses Methods,Functions,andClosures FunctionParameters GettersandSetters FunctionsasObjects Closures AsynchronousProgramming DataBinding BindinginMXML BindingExpressions What’sUndertheHood? BindinginActionScript BindingInsideaStringandApplicationParameters IsDataBindingaSilverBullet? PrograminStyleoranElevatorPitch FromCOBOLtoFlex FromJavatoFlex FromSmalltalktoFlex Object-OrientedActionScript ProgramDesignwithInterfacesandPolymorphism PolymorphismWithoutInterfaces NamespacesinActionScript UsingFlexwithJavaServerPages RetrievingDatafromJSP SendingDatafromFlextoJSP E4X,DataBinding,andRegularExpressions
59 60 61 63 65 66 68 69 70 73 76 79 79 80 83 84 85 86 86 91 92 93 94 95 97 97 99 100 101 101 103 104 106 110 112 115 115 118 122 RIAWITHADOBEFLEXANDJAVA
xi
TABLEOFCONTENTS Collections,Filters,andMaster-Detail AddingXMLListCollection Filtering Master-DetailRelationships AddingaDataFeed Events EventFlow EventPropagationorBubbling CustomEvents Let’sGoThroughtheSequenceofEvents SendingDataUsingCustomEvents Summary Endnotes CHAPTER5
ACompleteApplicationwithRPCCommunicationsandJMS Multi-TierApplicationDevelopmentwithFlex DesigningaStockPortfolioApplication AddingaDataGrid AddingtheChartingComponent Chart/DataGridToggling DealingwithFinancialNews ConfiguringtheServer-SideDestinationandProxy ProcessingtheNewsFeed IntroducingItemRenderers ProgrammingMaster-DetailRelationships AddingtheJMSFeedtotheStockPortfolio IntroductiontotheJavaNamingandDirectoryInterface IntroductiontotheJavaMessagingService TwoModesofMessageDelivery JMSClassesandTerms TypesofMessages HowtoPublishaMessage HowtoSubscribeforaTopic IntegratingFlexandJavaMessagingServices ConfiguringFlexMessagingDestination ConfiguringActiveMQJMS WritingtheTickerFeedJavaProgram ModifyingtheFlexClienttoConsumeMessages Summary
xii
RIAWITHADOBEFLEXANDJAVA
126 130 130 132 134 138 139 140 146 150 150 153 153 155 155 156 156 159 167 171 178 181 181 183 185 189 189 190 190 191 191 191 192 193 193 195 196 199 202
TABLEOFCONTENTS CHAPTER6
205
End-to-EndRapidApplicationDevelopmentwithFlex DataManagementServices FlexDataManagementServices:FlexRemotingonSteroids FlexDataServicesandAutomation:ProblemStatementandSolution A“Manual”FDSApplication BuildingtheClientApplication CreatingAssemblerandDTOClasses ImplementingtheFill-MethodoftheDataServicesDataAccessObject ImplementingtheSync-MethodofFDSDataAccessObject ImplementingUpdate,DeleteandInsertMethods IntroducingMetadata IntroducingTemplates MetadataforInputParameters TemplatesforImplementingtheFillMethod CompletingtheFillMethod SettingJDBCStatementParameters ReadingtheResultSetRecord TemplatesforImplementingSync-Method CompletingtheSyncMethod TheTemplateforthedoCreate()Method WhoOwnstheDAOFlexTemplates? RapidApplicationDevelopmentwithDAOFlex DAOFlexDirectoryStructureandConfigurationFiles DAOFlexProjectSetup RunningtheDAOFlexCodeGenerator TestingandUsingDAOFlexOutput Summary Endnote CHAPTER7
HowtoWriteYourOwnDataManagementServices SettingtheScene IntroducingDestination-AwareCollections MakingaDestination-AwareCollection SensingCollectionChanges AnatomyofManagedActionScriptObjects TheTwoFacesofChangedObject TrackingCollectionChanges MakingCollectionUpdateable
205 206 206 207 208 212 215 217 218 221 222 224 224 226 229 229 232 235 238 242 242 243 245 246 248 248 249 251 251 252 253 255 259 262 266 267 278
RIAWITHADOBEFLEXANDJAVA
xiii
TABLEOFCONTENTS TakingCareofBusiness:Transactions JavaBatchGateway:ManyRemoteCallsinOneShot BatchMember:OrderMatters BatchService ASampleApplication–AnOrderEntry OrderEntryDemoPre-requisites OrderEntryDemo:OrdersPanel OrderEntryDemo:OrderItemsPanel OrderEntryDemo:OrderManager Conclusion Endnotes CHAPTER8
EnhancingandExtendingFlexControls ComboBoxChallenges MakingtheValuePropertyWriteable AddingadataFieldProperty ComboBoxwithaMulti-ColumnList PopulatingaComboBoxwithServer-SideData EncapsulatingaRemoteObjectInsidetheComboBox AddingAuto-completeSupporttoTextInput IntegratingDataBaseSearchandComboBoxwithAutocomplete SeparatingBusinessResourcesfromGenericComponentCode BuildingaBusiness-SpecificComboBox BuildingandUsingtheResourceClasses Summary Endnotes CHAPTER9
TreeswithDynamicDataPopulation BasicsofTreeControl TheRoleofdataDescriptor MovingtotheRealAsynchronousWorld NoMoreFakeRemoting! DesignPatternsinOurLife DataTransferObject DataAccessObject AsynchronousCompletionToken AssemblerDesignPattern FaçadeDesignPattern
xiv
RIAWITHADOBEFLEXANDJAVA
285 287 292 293 297 298 300 302 304 307 308 311 311 312 314 318 321 325 332 339 343 353 354 356 361 362 365 365 366 369 370 374 374 375 376 377 377 378
TABLEOFCONTENTS WorkingwithEmployeesandDepartments TheDestination-AwareTree AddingCheckboxestoaTree CustomizingtheTreeItemRenderer TheDataBindingofCheckboxes Summary
379 381 385 387 393 396
CHAPTER10
WorkingwithLargeApplications DeploymentScenarios ApplicationDomains101 RuntimeSharedLibraries101 SWFsandSWCs:What’sUndertheHood MakingtheFlexLibrary.swc MakingaFlexApplicationApplication StaticversusDynamicLinking:DevelopmentPerspective So,YouSayDynamicLinking? Self-InitializingLibraries–Applications RecapoftheTechnique RSLversusCustomLoadingoftheDynamicCode TheCustomLoadingExample EmbeddedApplicationsandtheSWFLoaderObject ModulesandModuleLoaders WhenSizeMatters Summary Endnotes CHAPTER11
AdvancedDataGrid MakingDataGridDestination-Aware FormattingwithlabelFunction FormattingwithExtendedDataGridColumn IntroducingaComponentManifestFile MoreonCustomizingtheDataGridColumn ImprovingFormattingManager CheckBoxasaDrop-InRenderer DataGridColumnasItemRenderer’sKnowledgeBase NitpickingCheckBox RadioButtonGroupBoxasDrop-InRenderer ComputedColumnColor
399 399 400 401 401 403 404 405 408 409 412 416 421 422 428 433 438 438 439 441 441 442 444 446 447 449 450 457 458 461 467 476
RIAWITHADOBEFLEXANDJAVA
xv
TABLEOFCONTENTS ComputedColumnBackground RuntimeColumnStylesUnleashed MaskedInputandNumericInput DataGridwithAutomaticItemEditors Data-DrivenProgrammingUnleashed PivotedDataGridorPropertyBag Summary Endnotes CHAPTER12
LoggingandDebuggingFlexandJavaApplications Logging ConfiguringWebApplication(Server)Logging Client-SideLogging UsingtheLoggingAPI ServerLogTarget ServerLogTargetUsingPlainHTTP TheClient-SideTarget CreatingaTracingConsoleforLocalConnection TheRIAAspectofFlexLogging Debugging RemoteDebugging UsingEclipseWTPforDebuggingJavaEEServers ConfiguringTomcat DeployingaWebApplicationLocally Summary CHAPTER13
BuildingaSlideShowApplication ApplicationOverview DevelopingtheSlideShowComponent LoadingSlideShowData AnimatingtheSlideShow AddingInteractiveThumbnailNavigation DevelopingtheSlideShowPlayerApplication DevelopingtheSlideShowCreator IntegratingwithFlickrWebServices EditingSlideShowData DevelopingtheSlideShowPreview Summary
xvi
RIAWITHADOBEFLEXANDJAVA
479 481 487 494 498 503 515 515 519 519 520 520 522 523 524 526 528 530 532 534 534 537 538 541 541 543 543 544 545 547 550 552 559 560 561 563 566 570
TABLEOFCONTENTS CHAPTER14
573 573 575 580 582 583 592 601
DevelopingCustomCharts HowtoDevelopaChartfromScratch WorkingwithLargerDataSets AddingaVerticalLine Non-TypicalUseofSkinning AddingScrollingandZooming Summary CHAPTER15
IntegrationwithExternalApplications OverviewofFlashExternalInterfacingMethods UsingExternalAPI Two-WayFlex–OWCSpreadsheetIntegration FlexApplicationforOWCSpreadsheetIntegration HTMLTemplateEmbeddingOurActiveXComponents MakingFlexandSpreadsheetTalk HandlingFlexMetadataNotification SynchronizingSpreadsheetOWCwithFlexDataChanges SynchronizingFlexwithSpreadsheetDataChanges One-WayFlex-ExcelIntegrationviatheSystemClipboard FlexEmbeddedinMicrosoftExcel XMLFormatofExternalAPI LiveLink:StandaloneFlex-Excel LocalConnection101 FlexExcelAgentApplication FlexStandaloneApplication ExcelWorksheet–TheFlexAgentHost GettingintheNitpickingMood Summary Endnotes
INDEX
603 603 604 605 613 615 621 626 630 633 636 643 649 655 658 658 662 668 674 690 693 695 697
RIAWITHADOBEFLEXANDJAVA
xvii
xviii
RIAWITHADOBEFLEXANDJAVA
ACKNOWLEDGMENTS
Acknowledgments Writingatechnicalbookrequiresdedication,disciplineandwishpower.Butmostimportant,it requiressupportfromfamilymembers,andwe’dliketothankourfamiliesforunderstandingand especiallyourchildren,whogotusedtothefactthattheirfathersweregluedtotheircomputers, evenintheevenings. We’dliketothankallmembersofthevibrantandveryactiveonlineFlexcommunityfortheirdrive, energy,andeagernesstocontributetothesuccessofthetooloftheirchoice. We’dliketothankthetop-notchFlexprofessionalValerySilaevforhisvaluableinputtothechapter onintegrationwithexternalapplications,whichisoneofthemostcomplexchaptersinthisbook. We’dliketothankachartingguruVadimSokolovskyfromGreenPointforhishelpinwritingthe chapteraboutdevelopingcustomchartingcomponents. We’dliketothankourcontributingauthorBenStuckiforcreatingaverygoodslideshowcomponentandwritingachapterforourbook. Andmainly,wethankyou,ourreaders,forconsideringthisbook.
—YakovFain,VictorRasputnis,andAnatoleTartakovsky
RIAWITHADOBEFLEXANDJAVA
xix
xx
RIAWITHADOBEFLEXANDJAVA
FOREWORD
FOREWORD It’sriskytotrytodefineWeb2.0(whichmayormaynotbeatrademarkofO’ReillyMediaInc.), butthedescriptionI’veheardthatmakesthemostsenseisthatWeb1.0alwaysrequiredavisitto aservertomakeanychangestowhattheusersees,whereasWeb2.0downloadssomecodetothe clientsothat,forsometransactions,datacanbesenttotheclientinsteadofawholepage.The resultisaricherandfasteruserexperience.Ilikethisdefinitionbecauseitgivesmesomething concretetoevaluate. Thisisalogicalnextstepnowthattheprimitive,page-at-a-timeWebhasprovenitssuccess.At thesametime,it’sridiculousthatweshouldhavetodothisalloveragain.Afterall,beforetheWeb therewasalotofbrouhahaaboutclient/server,wheretheserverrunscodethatmakessenseon theserver,andtheclientrunscodethatmakessenseontheclient.Theideawasalotsimplerthan allthemarketingnoisewouldhaveyoubelieve,butitwasstillagoodidea. Ifthedefinitionaboveisvalid,thenonecouldarguethatthegenesisofWeb2.0isAJAX(AsynchronousJavaScriptAndXML).Ofcourse,JavaScripthasbeenaroundsince,effectively,thebeginning oftheWeb,butthebrowserwarsmadeJavaScriptinconsistentandthuspainfultouse.Akeypart ofAJAXisthatsomeonehasgonetothetroubleoffiguringoutcross-platformJavaScriptissuesso thatyoucanignoredifferencesbetweenbrowsers. Therearetwoproblemswiththisapproach.ThefirstisthatJavaScriptislimitedinwhatitcando. AJAXisanexcellenthackthatgetsthelastbitofmileageoutofJavaScript,butit’sahacknonetheless,andtheendisinsight.Thesecondproblemisthatyou’rerelyingonAJAXlibrariestohandle cross-browserissues,andifyouwanttowriteyourowncode,youhavetobecomeanexperton theseissues,andatthatpointAJAX’sleveragegoesoutthedoor.It’stoomuchtroubleandawaste oftimetohavetoknowallthegratuitousdifferencesbetweendifferentversionsofwhatissupposedtobethesameprogramminglanguage. YoucouldarguethatthesolutionisJava,becauseJavaisdesignedtobethesameonallplatforms. Intheorythisistrue.Intheory,Javashouldbetheubiquitousclient-sideplatform.Intheory,Lear Jetsandbumblebeesshouldn’tbeabletofly,andlotsofotherthingsshouldbetrueaswell,butfor variousreasonsJavaapplets,after10years,don’tdominateclient-sideautomation,anditseems unlikelythattheywillanytimesoon.
RIAWITHADOBEFLEXANDJAVA
xxi
FOREWORD
Inpractice,Javadominatesontheserver,sothisbooktakesthepracticalattitudethatmostpeople willwanttocontinuewritingtheserversideusingJava.Butforarichclient,thisbookassumes thatyou’veseenthatothertechnologieswilleitherrunoutofgasorinvolveaninstallationprocess thatconsumersresist(regardlessofhow“simple”thatinstallationprocessclaimstobe).Because theFlashplayerisinstalledonsome98%ofallmachinesandistransparentlycross-platform,and becausepeoplearecomfortablewiththeideaofFlashanddon’tresistinstallingorupgradingit, thisbookassumesthatyouwanttocreateyourrichclientinterfacesinFlash. Theonlydrawbacktothisapproachisthat,historically,Flashapplicationshavebeenbuiltwith tools that make more sense to artists andWeb designers than they do to programmers.This is whereFlexcomesin.FlexisaprogrammingsystemthatproducescompiledFlashapplications. TheprogramminglanguageinFlex,calledActionScript,isbasedonECMAScript,theECMA-standardversionofJavaScript,soanyJavaScriptknowledgeyoumighthaveisn’tlost.Youcanlearna singleprogramminglanguageandignorecross-platformissues. FlexcomeswithalibraryofUIwidgets,withapotentialmarketplaceforthird-partywidgets.As you’llseeifyoulookattheFlexdemosandtoursonwww.adobe.com,thesewidgetscanbevery functionalaswellasappealing,soit’spossibletoassembleaveryattractiveandprofessionalrich clientwithoutmucheffort. OneofthemainreasonsthatyoumightnothavetriedanearlierversionofFlexisthattheoriginal pricingmodelmeantthatyoucouldn’texperimentwiththetoolwithoutpayingaheftyfeeupfront. Abigchangehasoccurredsincethen,becausewithFlex2andbeyond,youcanuseanyplaintext editoralongwiththefreeAdobecompilerandFlexFrameworkcomponentstocreateyourrichInternetapplications.ThisincludestheabilitytohoststaticFlashapplicationsonyourserver.Adobe makes its money on the more powerful dynamic application server (which dynamically creates anddeliversFlexapplications)andtheEclipse-basedFlexBuilderenvironment,whichsimplifies thecreationofFlexapplications. I’dliketothinkthatIhadsomethingtodowiththemigrationtothefreecommand-linecompiler for Flex, since I spent an hour or so on the phone trying to convince a FlashVP (before Adobe boughtMacromedia)thattobecomepopular,programmersneededtobeabletoexperimentwith theplatformfreely.However,theresultismorethanIhopedfor. I think that Flex for rich Internet client applications can become a major player. Its easy crossplatformsupportremovesmanyprogrammerheadaches,thecomponentmodelofferspowerful libraryreuse,andtheresultproducesaverycomfortableandappealinginterfacefortheclientto use.BecausethisbookteachesyouhowtouseFlexalongwiththedominantserver-sidedevelopmenttool(Java),it’sanidealintroductionifyouwanttolearnhowtoleveragethesetechnologies. BruceEckel Author,ThinkinginJava4thEdition www.MindView.net
xxii
RIAWITHADOBEFLEXANDJAVA
FOREWORD
FOREWORD
At the beginning of the decade Macromedia coined the term rich Internet application (RIA) to describethefutureofapplications.AnRIAisaWebexperiencethat’sengaging,interactive,lightweight,andflexible.RIAsoffertheflexibilityandeaseofuseofanintelligentdesktopapplication andaddthebroadreachoftraditionalWebapplications.AdobeFlex2hasestablisheditselfasthe premiereplatformfordeliveringtheseexperiences. TheFlashPlayerisanidealruntimeforarichInternetapplication.Installedon98%ofWeb-enabled desktops,thePlayerhasareachfarbeyondanyindividualWebbrowserorotherclienttechnology. For years, developers took advantage of this by using the Macromedia Flash tool to build compelling, data-driven applications. But the Flash programming model wasn’t for everyone.When buildingwhatwouldbecomeFlex1.0,wegearedtheprogrammingmodeltowardsdeveloperswho hadmoreofaprogrammingbackground,especiallyJavaWebapplicationdevelopers.Wedesigned atag-basedlanguagecalledMXMLandinfluencedtheevolutionofActionScript2.0toappealto developers used to object-oriented programming. Flex 1.0 and Flex 1.5 were very successful for Macromedia,reachinghundredsofcustomersin18months,impressiveforav1productwithan enterprisepricetag. ButwerealizedthattoreachourgoalofamillionFlexdevelopersweneededtomakesomedrastic changesintheplatform.OurfirstchangewastorewritethevirtualmachineintheFlashPlayer fromscratchandimprovethelanguageatthesametime.TheAVM2insideFlashPlayer9isorders ofmagnitudefasterthanthepreviousvirtualmachineanduseslessmemory.TheActionScript3.0 languageisnowonaparwithotherenterprisedevelopmentlanguageslikeJavaandC#.Wethen re-architectedtheFlexFrameworktotakeadvantageoftheimprovementsourunderlyingfoundationexposed. Next, as Adobe, we decided to make the Flex SDK completely free. Developers can now create, debug,anddeployFlex2applicationsatnocost.TheFlexSDKprovidestoolsforcompilingMXML andActionScriptintodeployableapplications,buildingreusablelibraries,acommand-linedebugger,andadocumentationgenerator.WemadethismovetoremoveallthepricingbarrierstobecomingaFlexdeveloper.Wehopethatthisjump-startsthegrowthoftheFlexcommunity,andwe knowit’salreadypayingoffaswewatchtheincreaseintrafficonourforums,blogpostings,and mailinglistparticipation. RIAWITHADOBEFLEXANDJAVA
xxiii
FOREWORD
Ofcourseanydevelopmentexperienceisimprovedifyouhavearobustdevelopmentenvironment, andwebuilttheFlexBuilderIDEforthatpurpose.WedecidedtoleveragetheEclipseplatformfor FlexBuilderbecauseit’sfamiliartomanyofthedevelopersweenvisionusingFlex,andalsogaveus agreatstartingpointtobuildinganenterprise-qualityIDE.FlexBuilderprovidesprojectmanagement,codehinting,compilation,visualdebugging,andperhaps,mostimportant,aDesignView forrapiddevelopmentofyouruserinterface.WhileusingFlexBuilderiscertainlynotrequiredto buildyourFlexapplications,wethinkyourexperiencewillbemoreenjoyableifyoudo. AlloftheenhancementsmentionedsofaraddressthedevelopmentoftheFlexapplication,but don’tmentiononeofthetrueinnovationsintheFlex2release,FlexDataServices.FlexDataServicesisdesignedsoyoucaneasilyintegrateaFlexapplicationintoaneworexistingJ2EEsystem. TheremotingserviceletsyourFlexapplicationmakecallstoPOJOsorEJBs.Themessagingservice letsyourFlexapplicationconnecttoJMSmessagequeuesandtopicsandletsthosesystemspush messagestoallFlexclients.TheDataManagementServicemanagesdatasynchronizationbetween dataontheserverandyourFlexapplications.WhenyoumakeachangetodatainoneFlexapplication,it’simmediatelyreflectedinotherapplicationsandpersistedtoyourserver.Thesystem isscalable,highlyconfigurable,andintegrateswellintoanytransactionmanagementyoumight have,makingthissystemamustfordata-intensiveapplications. ThiswascertainlythelargestprojectI’veeverworkedon.Wehadengineersonbothcoastsofthe U.S.aswellasBangalore,India,workingtogetheronthisimmensesystem.Atanyhourduringa 24-hourdaysomeoneonourteamwasactivelydeveloping.Ofcourseit’snotjusttheengineers,or evenjustAdobeemployeeswhocontributedtothedevelopmentofFlex2.Fromthemomentwe releasedourpublicalphawe’vebeengettinggreatfeedbackfromthelargercommunity. TheprincipalsofFarataSystemshavebeenkeycontributorstoFlex’ssuccessviatheirparticipationinourbetaprograms,poststocommunityforums,publicpresentations,andblogpostings. Andnow,YakovFain,VictorRasputnis,andAnatoleTartakovskyarecappingthosecontributions withRichInternetApplicationswithAdobeFlexandJava:SecretsoftheMasters.Thismaynotbethe onlyresourceforlearninghowtoexperimentwithFlex,butitwillbeanimportantoneforbuilding real-worldFlexapplications. TheFlexproductteamspentalmosttwoyearstakingwhatwelearnedfromFlex1tobuildatechnologythatwouldrevolutionizethewaydeveloperscreateapplicationsthatengagewiththeirusersandwithinformation.NowthattechnologyishereintheformofFlex2andthisbookishereto helpyoutakeadvantageofallthatpower.There’salottolearn,butYakov,Victor,andAnatolehave doneanexcellentjobintroducingyoutoeverythingyouneedtoknowtobuildarobustapplication.WeatAdobecan’twaittoseewhatyoucreate. MattChotin ProductManager AdobeSystems,Inc. October2006
xxiv
RIAWITHADOBEFLEXANDJAVA
RIAWITHADOBEFLEXANDJAVA
xxv
xxvi
RIAWITHADOBEFLEXANDJAVA
CHAPTER
1
ArchitectureofRichInternet Applications
RIAWITHADOBEFLEXANDJAVA
1
CHAPTER1
ArchitectureofRichInternetApplications
WhatisarichInternetapplication? Historicallytherehavebeenmajorshiftsinthesoftwareindustry.Wemovedfrommainframeswith dumb terminals to client/server. Users gained inconvenienceand productivity,andmainframe systemswerepatronizinglylabeledaslegacy.WiththeavailabilityoftheWorldWideWebindustry, visionariesturnedthetables;vendorsandcorporateIThadbeeneagertogetridofthecomplexity ofclient/serverversionmanagementandtechnologistsweresoldonmulti-tiercomputing.This timeclient/serverwascalledlegacy.Andtorubitin,goodolddesktopapplicationswerelabeled “fat.” Excited with server multi-threading, messaging, persistence, and similar toys, we pretend nottothinkthat,attheendoftheday,we’dhavetotradeuserexperienceandproductivityforthe transparency of application deployment. And to make us feel better, we proudly called the new breedofapplications“thinclient.” Meetthenewchallenge:we’reenteringaneraofrichInternetapplications(RIA),whichrestoresthe powerofdesktopapplications…insidedownloadableWebpage.RIAsruninavirtualmachine(i.e., AdobeFlashPlayerorJavaVM)andhavethepotentialofbecomingafull-featureddesktopapplicationsoon.AsopposedtojustsimplydisplayingWebpagesdeliveredfromsomeservermachine, RIAreallyrunontheclient.Manyofthedatamanipulationtasks(sorting,grouping,andfiltering) aredonelocallylikeintheoldclient/serverdays.Déjàvuindeed!Industryanalystspredictthatin threeorfouryearsmostnewlydevelopedprojectswillincludeRIAtechnologies. ArichInternetapplicationcombinesthebenefitsofusingtheWebasalow-costdeploymentmodel witharichuserexperiencethat’satleastasgoodastoday’sdesktopapplications.And,sinceRIAs don’t require that the entire page be refreshed to update their data, the response time is much fasterandthenetworkloadmuchlower.Thinkofagloballyavailableclient/serverapplication. Let’sillustratethedifferencebetween“legacy”WebandRIAwithashoppingcartexample.NonRIAWeb applications are page-based. Since HTTP is a stateless protocol, when the user moves fromonepagetoanother,aWebbrowserdoesn’t“remember”theuser’sactionsontheprevious page.Asacommontreatmentofthis“amnesia,”auserstateisstoredontheserversideintheform oftheHTTPsession.
2
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
Considerthecaseofanonlineshoppingsession.Itcangoasfollows: 1. TheuserinitiatesasearchforanitemonWebpage#1. 2. Theserverprocessesthisrequestandreturnspage#2thatmay(ormaynot)containtherequireditem. 3. Theuseraddsanitemtoashoppingcartthattakesyetanothertriptotheservertocreatethe shoppingcartandstoreitontheserverside.Thentheserverrespondswithpage#3sothe usercaneithercontinueshopping(repeatingthefirstthreesteps)orproceedtothecheckout –page#4. Atthecheckouttheserverretrievesselecteditemsfromthesessionobjectandsendspage#5tothe userforshippinginfo.Thedataenteredtravelsbacktotheserverforstorage,andtheclientgets backpage#6forbillinginformation.Afterthatpage#7willconfirmtheorderandonlythengoes totheordercompletionpage. This simplest of online purchases consisted of seven roundtrips to the server. In a striking differencetodesktopapplications,afew-seconds-per-pagerefreshisconsideredfast(!)foratypical Webapplication,andthecommonlyacceptabledelayisuptoeightseconds.Istheusermotivated enough to complete the purchase?Think again, because your system gave him a chance to reconsiderseventimesinarow.Nowassumethatthenetworkand/orserverareslow.Result?Your potentialbuyerwentelsewhere. RichInternetapplicationseliminatetheroundtripsandsubstantiallyimprovesystemperformance bydoingalotmoreoftheprocessingontheclientthanathinclientWebapplication.Besides,RIAs arestateful:theyaccumulatetheinformationrightontheclient!Toputitsimply,RIAisn’tasetof pagescontrolledbytheserver;theyareactualapplicationsrunningontheclient’scomputerand communicatingwithserversprimarilytoprocessandexchangedata. Bothconsumer-facingandenterpriseapplicationsbenefitfrombeingRIAs.It’sawell-knownfact thate-commerceWebsitessuchasonlineticketreservationsystemsandonlineretailersarelosing revenuebecauseusersabandonshoppingcartsonnon-responsiveWebsitesduringthecheckout process.SuchWebsitesresultinlotsofcallstothecallcenter,amajoroperationalexpenseinand ofitself.TheperformanceofanysystemoperatedbyemployeesiscriticaltocompanyproductivityandRIAsprovideaperformanceboostoverHTMLapplications,whilereducingoperatingand infrastructurecosts.Finally,introducingwell-designedRIAsletscompaniesbuildbetter“mousetraps.” MacromediaintroducedtheexpressionrichInternetapplicationsbackin2002incontrasttothe bleakconditionofthe“legacy”Web,knowntodayasWeb1.0.AndyetthefirstRIAapplications were born as early as 1995 when Java was created. Java’s initial popularity manifested itself in a wave of small downloadable programs called Java applets. Applets, created with Java AWT (and later Swing) libraries, were run by the browsers’ JavaVirtual Machine (JVM). Ironically, the very technologythatmadeJavapopularwasn’texploitedandtodayJavashinesmostlyontheserverside andinmobiledevices. RIAWITHADOBEFLEXANDJAVA
3
CHAPTER1
In 2004Tim O’Reilly coined the catchy term Web 2.0.Which sites qualify forWeb 2.0 status has neverbeenclearlydefined.Ifsomeoneseesacool-lookingWebsite,it’sinvariablycalledWeb2.0. Labelingbetter-lookingWebproducts“2.0”isprettypopularthesedays.Oftenitreferstosocial engineering sites that let people collaborate and build the site’s content themselves.Wikipedia hasalengthyarticleonWeb2.0thatmaygiveyousomeideawhatthistermmeans.Seehttp:// en.wikipedia.org/wiki/Web_2.0. There’snowWeb3.0too,whichreferstotheSemanticWeb…butlet’sstopplayingthisnamegame andreturntothemainacronymofthisbook:RIA.
RIAPlatforms:TheMajorChoices It’snothardtoguessthatthere’smorethanonewaytocreateRIAs.Aswesaidearlier,RIAsrunin theclient’sbrowserwiththehelpofsomekindofclientengine. Forexample,thesearethemostpopularproductsortechnologies: UsingFlexyoucancreateanActionScriptapplicationfortheubiquitousFlashPlayer,ahigh-performance multimedia virtual machine that runs bytecode files in the SWF format (pronounced swif ).Theplayer’sJITcompilerconvertstheSWFbytecodetonativemachinecodeforfastperformance.ThelaterfacilityisspecifictoFlex2,availablesince2006.AlthoughearlyversionsofFlex wereoutin2004,theydidn’tsupportjust-in-timecompilation. AJavaprogrammercancreateJavaapplets.Asmentioned,thissolutionhasbeenavailablesince1995. WindowsPresentationFoundation(WPF)wasreleasedaspartof.NET3.0inNovemberof2006 andcanbeusedtocreatebothInternetanddesktopapplications. Finally,there’sAJAX,akaDHTML,circa1998.ThissolutionwasrecentlyboostedwithXMLHttpRequestAPIsupportforallmajorbrowsers.AJAXservedasawake-upcallfortheuseranddeveloper communities.ItisoftenthefirststeponthemigrationpathfromthelegacyWebtotheworldofRIA despitebeingseriouslyhandicappedbyhavingtosupportbrowserincompatibilitiesandapoor programmingmodel.
AdobeFlex2 Flex 2 applications run cross-platform in a ubiquitous Flash Player 9 that’s a lightweight virtual machine.Theplatformincludes: • AnXML-basedlanguagecalledMXMLthatsupportsthedeclarativeprogrammingofGUI componentstargetingdesigners • Thestandardobject-orientedprogramminglanguage,ActionScript3.0,basedonthelatest ECMAScriptspecification • Server-sideintegrationviaFlexDataServices(FDS)givingclientapplicationstransparentaccesstotheworldofJ2EE
4
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
• •
Chartingcomponents,accesstomultimediacontrols,etc. AnEclipse-basedfull-featuredIDEwithautomateddeployment,debugging,andtracing facilities
TheFlex2platformiseasilyextendableandintegrateswellwithserver-sideJava,ColdFusion,PHP, Ruby,ASP,andthelike.TheupcomingreleaseofAdobeApollowillallowthecreationofadesktop applicationbasedonFlashPlayer,Flex,PDF,andHTML. TheSWFfileformatisopen,andtherearethird-partyopensourceproductsthatoffertoolsfor creatingRIAsdeliveredbyFlashPlayerlikeOpenLaszlofromLaszloSystems. Asopposedtothelastversion,Flex2offersawaytocreateRIAswithoutincurringheftylicensing fees.Thisiswhatcomesatnocost: • • • • • •
MXML:AnXML-baseddeclarativeprogramminglanguageforcreatingaGUI ActionScript3.0:Anobject-orientedlanguagesimilartoJava FlashPlayer9:AvirtualmachinewithatinyfootprintthatlivesinsideaWebbrowserand runsyourcompiledbytecode(.SWF) Command-linecompilersanddebugger FlexFramework:Includesalibraryofwell-designedGUIcomponent:buttons,tabfolders, datagrids,treecontrols,animatedeffects,andmore FlexDataServicesExpress:TemplateWebapplicationdeployedinaJ2EEservertocommunicatewithanActionScriptclientapplicationrunbyFlashPlayer.FDSExpressislimitedtoa singleCPUandisnotsupposedtobeusedinahigh-availability(24x7)configuration
ThefollowingFlextoolsrequireapurchasedlicense: • • • •
FlexBuilder–theEclipse-basedIDE Chartingcomponent FlexDataServicesDepartmental,24x7,100concurrentusers FlexDataServicesEnterprise,24x7,unlimitedusers
TheprocessofcreatingabasicFlex2applicationconsistsofthefollowingsteps: 1. DesigntheapplicationbyaddingMXMLcomponentslikethisbutton:
IfyouusetheFlexBuilderIDE,youcanapplydrag-and-droptechniques.Alternatively,youcan writetheMXMLastext. 2. WritethecodeinActionScriptperyourfunctionalspecification,forexample: private function processOrder (event:Event):void{ RIAWITHADOBEFLEXANDJAVA
5
CHAPTER1 //The business logic goes here }
3. Compilethecode.FlexcompilerautomaticallyconvertsMXMLintoActionScriptandcreates bytecodeoutputintheformofanSWFfiletoberuninFlashPlayer9orabove.You’llenjoya fullyautomaticcompilationprocessifyouusetheFlexBuilderIDE. 4 DeploytheSWFfileandthewrappingHTMLpageintheWebserverofyourchoice.ThedeploymentprocessandcreatingthewrappedcanbecompletelytransparentifyouusetheFlex BuilderIDE. ThereisaFlexOnlineCompilerWebsitewhereyoucantrywritingFlexcodeonlinewithoutinstallinganythingonyourcomputer.Thereareevencodesnippetsreadytobemodifiedandrun.See http://try.flex.org/. MoreadvancedFlexapplicationscanincludeinteractionwiththeserver-sidesystemsthroughFlex DataServices,whichprovidesremoteaccesstoserver-sideJavaobjectsandJavaEEcomponents, extensive messaging support (including JMS integration), synchronization with persisted data, andintegrationwithotherpersistenttechnologies.
Figure1.1FlashPlayerandFlexDataServices
Java EventhoughtheJavaprogramminglanguagebecamepopularlargelybecauseofappletsandthe famousdancingDuke(http://java.com/en/download/help/testvm.xml),appletshaven’tbecome Java’smainusepattern.Themainreason:thelargefootprintoftherequiredJVM(currently16MB). Andthereareotherdrawbacks.Forinstance,althoughJavaSwingwasmeantforaplatform-independentlook-and-feel,absentanygood-lookingoff-the-shelfGUIwidgetsitwashardsellingitto thepublic.InthisregardtheFlashandFlexcreatorsdidamuchbetterjobwiththeireye-candy components.Ortakeaudioandvideointegration.Todaypeopleareusedtohavingstreamingau-
6
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
dioandvideocomponentsembeddedinWebpages.ButthemultimediaJavaAPIremainsrudimentary,tosaytheleast. TherearesomeeffortstominimizethesizeoftheJVMusedbyWebbrowsersandtheJavaBrowser Editionprojectnowneeds“only”about3MBtorunaprimitiveHelloWorldapplet.Butthiscan’t competewithFlashPlayer9,whichmanagedtoaccommodatetwovirtualmachinesina1.2MB downloadthatcanrunanyRIA,howevercomplex. AnotherissuewithJavaappletsisthattheydon’tofferaseamlessdownloadoftheproperversionof theJVMalongwiththeapplet.FlashPlayer’sexpressinstalldoespreciselythat. Havingsaidthat,wemustacknowledgethatJavaSwingisaverymatureandrobusttechnology forcreatingGUIapplicationsdeliveredeitherovertheWeborinstalledonthedesktop.Youcando literallyanythingwithJavaSwing–ifyoucanaffordit.No,youdon’tpaylicensingfees,butbecause ofthelongerdevelopmentcycleandtheneedtoengageexpertprogrammers,industrial-sizeSwing projectsareusuallyquiteexpensivetobuildandmaintain.
WPF Microsoft’s Windows Foundation Platform, or WPF, become available with the release of Vista (http://msdn2.microsoft.com/en-us/netframework/aa663326.aspx ). It uses an XML-based declarative programming language called XAML to create GUIs and C# as a general-purpose programminglanguage.WPFissuitableforcreatingbothRIAanddesktopapplications.XBAPstands forXAMLBrowserApplicationandit’saWPFwayofcreatingRIAsthatruninInternetExplorer. MicrosoftisplanningtoreleaseaversioncalledWPF/Ethatwillrunonsomenon-Windowsplatforms.While living in a sandbox, XBAP will have access to all .NET 3.0 functionality butWPF/E won’t.WPF/EusesXAMLandJavaScript(noC#).CommonLanguageRuntime(CLR)istheclient’s WPFengine. TocreateWPFapplications,developerscanuseMicrosoft’sVisualStudio2005IDEwithinstalled .NET3.0extensions.ThenextversionofthisIDE,calledOrcas,willincludeavisualGUIdesigner. WPFdevelopersusethesamecodebaseforwritingXBAPanddesktopapplications:theyjustenclosethecodesectionsthataren’tallowedinXBAPintotheifdefblocks. MicrosoftXAMLcoderesemblesAdobe’sMXML.Eventhoughtoday’sFlex2isalotmoremature thanWPF,Microsofthasanestablisheddeveloperbase,whileAdobetraditionallycateredtodesigners.Itsmaingoaltodayistoconvinceenterprisedevelopers(particularlytheJavacamp)that FlexcanbeatoolofchoiceforcreatingbusinessRIAs.
AJAX WhilethetermAJAXwascoinedbyJesseJamesGarretinFebruaryof2005andispartlyrootedinthe asynchronousXmlHttpRequestimplementedbyMozilla,lotsofdevelopershaveusedMicrosoft’s versionofXMLHttpRequestandalternativetechniqueslikeIFramesince1999.Thesetechniques RIAWITHADOBEFLEXANDJAVA
7
CHAPTER1
facilitatesynchronousandasynchronouscommunicationsbetweenthescriptinapageandserver-sidecode.ThemainproblemwithAJAXisthatdespiteitspopularity,ithasnotechnicalfoundation.Whiletheothersolutionswementionherearebasedonrock-solidvirtualmachines,there’s no standardVM for AJAX. Each browser implements AJAX building blocks differently. There’s a chancethatadeployedAJAXapplicationwillrequirecodechangeswitheachnewbrowserrelease. Wait,let’srephrasethat:there’sachancethatdeployedAJAXappsmayrunasisonanewbrowser release.Doyouwanttotakechanceswithyourbusiness? Thatsaid,InternetgiantslikeGoogle,Yahoo,andAmazonarebuildingAJAXappsontopoftheir ownabstractionlayerssuchasGoogleWebToolkit(GWT).Becauseoftheimmaturelevelofthe technology,theseabstractlayersneedconstantvendorattentionassoonaschangesappear.
AJAXShortcomings Anabilitytocreateflicker-freeWebappswithoutbuyingmoresoftwareisAJAX’sbigappeal.You mayhaveheardthechant,“AJAXisfree.”Here’sthesimpletranslation:nocommercialAJAXtool isworthpayingfor.Therearehundredsoflibraries,toolkits,andcontrolsetsthatgiveyoutheimpressionthatAJAXapplicationsarecheaptodevelopandstrategicallysafesincethere’snovendor lock-in.Actually,thereisvendorlockingbecauseyouwon’tmanuallywriteJavaScriptcodeandwill havetopickanAJAXlibraryofsomevendor.Nowthinkaboutit:startingfromthegroundupyou needacommunicationlayer,messagingandremotingmechanisms,anHTTPsniffer,alibraryof UIcomponentswithsharedobjectsandeventmodels,avisualIDEthatunderstandsthesecomponentsindesigntime,andadebuggerthataccommodatesallthisstuff.Ontopofthat,there’sinternationalizationsupport,accessibilityforthedisabled,andsupportforautomatedtestingtools. Youreallythinkyou’resafewithmix-and-matchfromdifferentvendors?Iftheanswerisyes,you mustbeworkingforasoftwarecompanyintheRIAbusiness.Comingtoreality;alongdevelopmentcycle;lackoffree,qualityGUIcomponents;andtheshortcomingslistedbelowmakeAJAX lessappealingand,actually,themostexpensivewayofcreatingRIAs. ThesearesomeofAJAX’scurrentdrawbacks:
8
•
JavaScriptdevelopmenttoolsarelimitedduetothedynamicnatureofthelanguage,and debugginganyDHTML/JavaScriptmixisapain.Yes,Google’sGWTcanspareyoufromwritingJavaScriptmanually,butattheendoftheday,it’sstillJavaScriptthathastobedeployedin production.Whenthesystemisn’tworkingandtimeislimited,whatareyougoingtouseto debugit–therealpageofJavamock-up?
•
TonsofJavaScriptsourcecodehastogooverthewiretotheclienttobeinterpretedbythe browser.We’retalkingaboutbusinessapplications,notsomeproof-of-conceptdemo.
•
WebbrowserswillhappilydisplayyourapplicationevenifapieceofJavaScriptdidn’tarriveat theclient.Youwon’tknowifaproblemexistsuntilyouexecutetheparticularusecase.
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
•
Asimpleright-clickfollowedbythe“ViewSourcecode”menuoptionwouldrevealyourbusinessapplicationcode.Betteryet,allthiscoderesidesasplaintextinthebrowsercacheon disk.Becauseofthis,youhavetodropallthecodecommentsanduseobfuscatorstoprotect yourcodefrombeingstolen.
•
HTMLrenderingisslow:thinkofadatagridthatcontains5,000records.Where’syourmutual fundreport?
•
AnydatamanipulationbyJavaScriptisinherentlyslowbecauseJavaScriptisaninterpreted, notacompiledlanguage.We’retalkingthousandoftimesslow.
•
Thecodeismorevulnerabletohackerattack,afactthatwasprovedrecentlybyawormthat stoleabunchofmailaddressesfromYahooaddressbooks.
•
AJAXdoesn’tsupportserverpush.Theserver-sideapplicationcan’tpublishthedatadirectly totheclient.AJAXapplicationshavetopollthedatafromtheserveratspecifiedtimeintervals withoutknowingifthedataisthereornot.
DionHinchcliffe,theeditor-in-chiefofAJAXWorldMagazine,listedsomeoftheimportantthings everyAJAXdevelopershouldknow(seehttp://web2.wsj2.com/seven_things_every_software_project_needs_to_know_about_AJAX.htm): • • • • • • •
BrowserswerenevermeanttohandleAJAX. Youwon’tneedasmanyWebServicesasyouthink. AJAXisharderthantraditionalWebdesignanddevelopment. AJAXtoolingandcomponentsareimmatureandthereisnoclearleadertoday. GoodAJAXprogrammersarehardtofind. IttakesrealworktomakeupforAJAX’sshortcomings. AJAXisonlyoneelementofasuccessfulRIAstrategy.
Hinchcliffe says,“The addition of RIA platforms such as Flex, OpenLaszlo, andWPF/E to a RIA strategyisvirtuallyrequiredtoproperlyexploittherangeofcapabilitiesyou’llwantcapableonline applicationstohave.Thisisparticularlytruearoundrichmediasupportsuchasaudioandvideo –whichAJAXisvirtuallyincapableof–butevensuchmundanethingsasgoodprintingsupport. TheseareallthingsthatthemoresophisticatedFlash-basedRIAplatformsreallyshineatandare easiertoprogramintoboot.AJAXwillincreasinglygetaseriousrunforitsmoneyfromtheseplatforms,particularlyastheyprovideback-endserversupportforthingslikeserver-sidepush,formal WebServices,enterpriseenvironments,andmore.” Interestinglyenough,thefutureofAJAXintheenterpriseapplicationspacemaynotbeasbleakas itappears,andwe’lltellyouwhyalittlebitlaterinthischapter. Finally,thebrowser’sgoldendaysarealmostover.Allleadingsoftwarevendorsarelookingbeyond thebrowserforthenextWebapplicationplatform. RIAWITHADOBEFLEXANDJAVA
9
CHAPTER1
Tosummarize,ifyou’redevelopinganewenterpriseRIAfromscratch,AJAXmaynotbethewayto go.Chooseasolidapplicationdevelopmentenvironmentthatoffersavirtualmachineatruntime likeFlex/Flash,Java,orWPF.AnyoftheseenvironmentsismoreproductivethanAJAX.IfyoualreadyhaveAJAXapplications,youcannicelyintegratenewFlexRIAsinexistingAJAXapplications usingtoolslikeFABridgefromAdobeforcommunicatingbetweenAJAXandFlex. DuringAJAX’sfirstyearoflife,everyarticleorbookonthesubjectmentionedGoogleMapsand Gmail,andvarioustype-aheadsamples:youenterthefirstzipcodedigitinatextfield,anditsuggestsyourpossiblechoicesbasedonyourinputwithoutapagerefresh.Today,youcanreadabouta numberofAJAXapplications,includingonesthatworkwithphotoimagesloadedfromthepopular flickr.comWebsite.Ifyourunintooneofthesesamples,compareitwiththeslideshowapplication inChapter13.Thisisprobablytheeasiestwaytoseethedifferencebetweentherichandjustalittle richerInternetapplications. Last, we’d like to make it clear that popular comparisons of Flex versus AJAX are simply wrong, sinceFlexisaframeworkandacompletedevelopmentplatform,whileAJAXisasetoftechniques. Tocompareapplestoapples,youshouldcompareproductslikeFlexagainstGWT(Google)and thelike.
OtherRIASolutions Inthissectionwe’veincludedshortdescriptionsofseveralotherplayersintheRIAspace.
OpenLaszlo OpenLaszlo(http://www.openlaszlo.org)fromLaszloSystemsisanopensourceproductthatlets youcreateapplicationsthatcanbedeployedasDHTMLorFlashPlayerfiles(checkouttheproject calledLegalsathttp://www.openlaszlo.org/legals).TheabilitytogenerateDHTMLcodemadeita goodcandidatefordevelopingapplicationsformobiledevices,andSunMicrosystemshasrecently partneredwithLaszloSystemstobringthistechnologytotheJavamobilespace.ThisisdirectcompetitionforAdobeFlashLite. FlashPlayerisbecomingade-factostandardinthedeliveryofmultimediaapplications,andOpenLaszloisalreadyusedbythousandsofRIAdevelopers.LikeFlexandWPF,youdefinetheGUIwidgetsinadeclarativeXML-basedlanguagecalledLZX.TheprocessinglogiciscodedinJavaScript. OneofOpenLaszlo’ssellingpoints,besidesbeinganopensourcesolution,isthatyoucancreate applicationsforversionsofFlashPlayerasoldas6.0,whichyoucan’tdowithFlex2.Butthehigh speedofpenetrationandadoptionofthelatestversionofFlashPlayerdiminishesthevalueofthis benefit.
GWT GWT stands for GoogleWebToolkit (http://code.google.com/webtoolkit/). It lets you write programsinJava,whichareautomaticallyconvertedtoJavaScriptsotheycanbedeliveredasAJAX
10
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
Webapplications.Thisisn’tthefirstattempttoofferatoolthatconvertsJavatoJavaScript.Takethe opensourceprojectJava2Script(http://j2s.sourceforge.net/),anEclipseplug-inthatimplements anEclipseSWTlibraryinJavaScript.ButGWT’ssuccessisanotherillustrationofhowimportantit istohavesupportfromacommercialsoftwarevendor,suchasGoogle,foranyAJAXinitiative.GWT isnotanopensourceproduct,butitisfree. AninterestingGWTfeatureisthatitcompilesJavaintovariousversionsofJavaScripttoaccommodatethespecificneedsofdifferentWebbrowsers.GWTcomeswithalibraryofextensiblecomponents.Whenthischapterwaswritten,thenumberofthesecomponentswaslimited,butthat willprobablychangesoon,becauseit’sGoogle.ItsUIcomponentsareJavaclasses(i.e.,MenuBar, HiperLink,Tree,andFileUpload),whichcanbecombinedintocontainers(i.e.,HTMLTable,SimplePanel,andComplexPanel). GWT’shostedWebbrowserletsJavadeveloperscreateandtesttheirapplicationswithoutconvertingthemtoJavaScriptuntiltheapplicationisreadyforreal-worlddeployment.So,ifaJavadeveloperforwhateverreasonhastoworkonAJAXapplications,heshoulddefinitelyconsiderusingthe GWTframework.
Nexaweb Nexaweb(http://www.nexaweb.com)offersaJava-basedthinclientthatdoesn’trequireanyadditionalinstallationontheuser’sdesktop.Theapplication’sstateiscontrolledbyasmallJavaapplet running on the client.This applet communicates with the Nexaweb Java EE application as needed.ToavoidissuesrelatedtotheversionoftheJavaRuntimeEnvironmentinstalledwiththe Webbrowser,NexawebusesJRE1.1foritsapplet.It’ssupportedbyeverymajorbrowser.ThisappletpartiallyupdatestheWebpagewhenthestateoftheapplicationchanges.Theuserinterfaceis definedusingadeclarativeXML-basedlanguage,andtheapplicationlogicisprogrammedinJava. ItcomeswithanEclipse-basedvisualeditor.
Canoo Canoo(http://www.canoo.com/)offersaso-calledUltraLightClient,whichisaJavalibraryofserver-sideproxyclassessimilartotheSwingAPI,providing,forexample,ULCTableinsteadofJTable. Basicallyeachoftheseclasseshastwopeers–onefortheserverandtheotherfortheclient’sJRE. Thelibrarytakescareofthesplitbetweenclientandserver,includingsynchronizationandcommunicationofthesehalvesusingaproprietaryprotocol.Asmallpresentationenginerunsonthe client,whiletheapplicationrunsontheserver.Theclient’spresentationlayercanbedeployedas aJavaappletorapplicationusingJavaWebStarttechnology.Theserver-sideapplicationcanbe deployedasaJavaservletorastatefulsessionbean.
Backbase Backbase(http://www.backbase.com)includesaverylightenginewrittenentirelyinJavaScript. Itloadsseamlesslyatthebeginningoftheclientsessionandbringsintheclient-sidepresentation framework,whichincludestoolstomaintainstateontheclient,client/serversynchronization,and incrementaldataload.Thepresentationframeworksupportsdrag-and-drop,databinding,styling, skinning, and multimedia. Backbase also steps away from typical page-basedWeb applications RIAWITHADOBEFLEXANDJAVA
11
CHAPTER1
andletsyoucreateaso-calledSingle-PageInterface.Itcomeswithalibraryofmorethan50offthe-shelfGUIcomponentswrittenusingDHTML,JavaScript,CSS,andXML.
Apollo,Desktop2.0,andtheBrightFuture We’vebeencloselyfollowingtheprogressofAdobe’sApolloproject(http://labs.adobe.com/wiki/ index.php/Apollo),whichwillmakepeopleswitchfrompureWebapplicationstodisconnectedor partiallydisconnectedones.ApollowillintegrateFlashPlayer,Flex,HTML,JavaScript,andeventuallyPDF,andit’llprovideaccesstoalldesktopresources(filesystem,ports,etal).Thismeansthat prettysoonWebapplicationswillspillfromtheWebbrowserrightonyourdesktop.Yes,we’removingtodesktopapplicationsagain–thistime“Desktop2.0.” Ashistoryrepeatsitself,let’srecap. Desktop1.0arrivedmorethat20yearsagoasanintelligentandjazzyreplacementfordumbmainframeterminals.Forthefirstfewyears,oneofitsmainfunctionswasterminalemulation.Afew killerapps(Lotus1-2-3andWordStar)changedthewaypeopleenvisionedusingcomputers.They realizedthattheycouldhavetheirownprivatedatastoresavedonahomecomputer!Afterawhile asetofclient/servertechnologiesemerged.Themainsellingpointofclient/serverwastheeasein gettingserverdatatotheenduserinanonlinemodeasopposedtothebatchmode,whichdeliveredthedatarequestedthenextdayorlater.Mainframeterminalswiththeirgreen-on-black-textonlyuserinterfaceweresentencedtooblivion. VisualBasic,VisualC++,andPowerBuilderwerethetoolsofchoiceduringtherapidgrowthofthe client/serversystem.Astheworld’sappetiteforcomputerapplicationsgrew,deployingandversioningbecamemoreandmorecostly. ThentheWebwithitsWeb1.0thinclientstartedremindingusofthedumbterminalagain,butthis timethebackgroundwaswhite.Asemi-intelligentWebbrowserprovidedonlineaccesstoaplethoraofJ2EE,LAMP,or.NETserverslocatedaroundtheworld.TheapplicationswereallonebigshoppingcardanduserswerereducedtotheOKandCancelbuttons,butthatwasanaffordablewayfor everyonetoselltheirservicesontheWeb.Lateron,businessesbegancreatingmoresophisticated Webapplicationsandevenconvertedthecompleteclient/serversystemintoDHTML/XmlHttpRequest,albeitnotlabeledAJAXyet.(SeriouslywhattookJesseJamesGarrettsolong?)Andnowwe’re approachingthe“client/Web”revolution–thistimemakingallthedataintheworldavailableand personalatthesametimewiththeeye-candylook-and-feelofrichInternetapplications. TheworldshouldnowcomeupwithnewkillerappsandrevisitoldonesbutinaWeb/Desktop2.0 context.Inthecaseofwordprocessororspreadsheetcollaboration,privacycanbemoreimportantthantheabilitytodoaspellcheck. Makingapplicationdevelopmentsimplerwillgetushalfwaythere.Smallteamsofdeveloperswill beabletocreateOffice-likecomponentsinFlexandApollo.Weseeanothertrendhere:puttingthe enduserinchargeoftheGUIexperience.Forexample,manybrowsershortcomingswere“fixed”
12
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
byaddingtoolbarsandcomponents.CreatingaRIAplatformandapplicationscapableofcrossintegrationisgoingtobevital. PortingGoogleMapsintoadesktopApolloapplicationwasdemo’dattheMAX2006conference. Expecttoseeanumberof“webified”desktopapplicationsbymid-2007.Theauthorsofthisbook areworkinghardonFlex/Apolloapplicationsand,hopefully,you’llseetheresultatwww.faratasystems.comwhenApolloisofficiallyreleased. ThebirthofApollohasaninterestingtwist:bothFlexandHTML/JavaScriptapplicationscanbe portedtoApollo,whichmeansthatAJAXapplicationswillfindtheirnewhomeinApollotoo.Not onlytheywillbeported,they’llbeupgradedandturnedintodesktopsystems.Basically,Apollowill offerAJAXalifeline.
SomePragmaticFlex/JavaConsiderations Cooltechnologiescomeandgo.Onlysomeofthemsettledowninthetoolboxofaprofessional programmerworkingonenterprisebusinessapplications.Thetechnicalexcellenceofanysoftware isimportant,butit’snottheonlycomponentinitssuccess.
TheLearningCurve:ReuseofSkillsandCode Oneimportantconcernofanydevelopmentmanageristheavailabilityofalargepoolofpeople whoknowaparticularsoftware.ThereareplentyofJavaprogrammerswiththeskillsrequiredto developWebapplications,andthegoodnewsisthatenterpriseJavadeveloperswithservlet/Java EEprogrammingskillsorJavaSwingexperiencewillfindtheFlexpathveryeasy.JavaandActionScript3.0areverysimilar;EclipseisafamiliarenvironmentformanyJavadevelopers.SinceFlex piggybacksonJavaEEandbrowsertechnologies,aserver-sideJavaprogrammershouldbeable tocorrelatehiscurrentskillswiththedevelopmentprocessinFlex.JavaSwingdeveloperswillinstantlyspotsimilaritiestoJavaeventandlayoutmodels.Inourexperience,thetypicalramp-up timeformotivatedJavadeveloperistwoweeks.ThebestpartisthatFlexseamlesslyintegrateswith existingJavaservercodeofanyflavor–POJO,EJB,Hibernate/Spring,orJMS,sotheserver-side partcanliterallyremainunchanged.
ApplicationSecurity AnotherimportantconsiderationisthesecurityofFlex/Javaapplications.Flexserver-sidesecurity managementisquiteextensive,anditletsyouuseeithercontainerofferingsorcustomsecurity providersviadeclarativeXMLbinding.ThereareacoupleofchallengessinceFlexsupportsmultipleprotocols,soJavaEEsecuritythatreliesonlyonHTTPsessionshastobeextended,butit’sa simpleandwell-documentedprocess.Ontheclientside,itbuildsonFlashPlayersecuritythat’s knownforitslackofserioussecurityflaws.Youhaveabuilt-insecuritymanagerthathasallthe standardprotectionforcross-domainandzoneaccess.Corporationscanfurtherrestrictthecode theygetfromthirdpartiesbywrappingthecodeloadersinadditionalsecuritymanagers.
RIAWITHADOBEFLEXANDJAVA
13
CHAPTER1
FlexGUIPerformance JavaSwingisatriedandtruetoolfordevelopingresponsiveGUIsindemandingapplicationslike stocktradingandonlineauctions.Flexiscapableofprovidingnear-real-timedatarenderingtothe GUIandveryhighrefreshratesonlargedatasets.FlashPlayer9isthehigh-performancemodern virtual machine with precompiled optimized code and a just-in-time (JIT) machine code compiler.
ShorterDevelopmentCycle It’sverypossibletobuildtheclientsideofareal-timeportfoliodisplayintegratedwithnewsfeeds andgraphsinabout200linesofFlex2code.AsimilarSwingprogram(evenifit’screatedwithcommercialIDEs)wouldbeseveraltimeslarger. Therealsellingpointbecomesobviousonthesecondorthirddayoftheproof-of-conceptphase. AdecentFlexdevelopershouldbeabletoprototype(shortofserverprocessing)mostoftheUIfor aspecifictradingsystembytheendofthefirstweek.Ifyou’reluckyandsystemintegrationwent okay,youcanaddthecollaborationfeaturesandmultimedia–withthetotalclientcodebasefor thewholeprojectcomingwithin1,000-1,500lines.ThisisdefinitelynotpossiblewithSwing.Of course,somepeoplewillsaymodernJavaIDEsgeneratelotsofboilerplatecodeautomatically,but thisisstillthecodethatsomeonehastoread,understand,andfixifneedbe.
RoomforImprovement WhilemanyprogrammerswhodealtwithpreviousversionsofFlexarereallyhappynowthatthey haveaprofessionalEclipse-basedIDE,Javaprogrammersarespoiledbythevarietyofexcellent and responsive IDEs. Flex Builder is a truly RAD tool, and it helps tremendously in writing Flex code,butwhenthiswaswrittenitworkedabitslowerthantheEclipseIDEforJava.Oneofthe reasonsFlexseemsslowerthanJavaisthatitdoessomuchmorethantheJavacompiler.Ituses anumberofcodegeneratorsbehindthescenes,amulti-passcompiler/linker,andadeployment optimizer.Italsoembedsafull-fledgedFlash“projector”inEclipse.
ConnectionManagement FlexprovidesanextensiveinfrastructureformanagingconnectionsbetweentheWebbrowserand theserver.ItletsyouspecifyalltheprotocoldetailsfortheRealTimeMessagingProtocol(RTMP), HTTP,HTTPS,aswellasplainsocketconnections.Youcanthenspecifytheorderinwhichtheprotocolsshouldbetriedduetoavailability/firewallissues.Flexalsoautomaticallybundlesmultiple requestsgoingoverthesametransport,strivingformaximumperformanceout-of-the-box.Since anyWebbrowser,bydefault,maintainsonlytwoconnectionswiththeserver,Flexgivesyoumuch betteruseofthesetwoconnectionsifyoulimityourselftoHTTP.Youalsogetalternativereal-time pushorpullconnectionsonadditionalprotocols,adirectsocketAPI,andtonsoflow-levelhooks thatletyoubuildanyprotocolyouwant. WeworkedonAJAX-likeframeworksduring1999-2005andcansaythatFlexisalotmorerobust
14
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
incodeanddatadeliveryfieldsthananycross-browserAJAXsolutionknowntoday.Robustnessis anevenbiggershowstopperforlargeAJAXapplicationsthanperformance.Flex/Flashreallygives backthecontrolovercommunicationsthatisveryimportantforenterpriseapplications.
FlexandAgileDevelopment Inthemid-’90sPowerBuilderandVisualBasicwerethetoolsofchoiceintheclient/serverfield. Software developers didn’t really worry about what was under the hood, but more importantly, theywerebusinessusers’bestfriends.Theycoulddostuffquickly,orusingthemodernjargon, theywereagileprogrammerswithoutevenknowingit.They’daskthebusinessuserJoe,“Howdo youusuallydoyourbusiness?Whatwouldyouliketohaveonthisscreen?Describethestepsof yourbusinessprocess.”MostlikelyJoewouldn’tknowalltheanswers,butthiswasokay,because developerscouldcomebacktotheuserthenextdaywithaworkingprototype.Thiswaseasywith PowerBuilder’sDataWindow.WhenJoe-the-usersawtheprototype,hisglassylookallofasudden would become friendly and understanding. NowJoewasbackincontrol:“No,youdidthispart wrong,Iwanteditdifferent.”Noproblem,developerswouldreturnwiththechangesthenextday (notnextmonth,butthenextday!). Developers didn’t really know how the DataWindow worked, but they trusted this component. PowerBuilderusedanevent-drivenprogrammingmodel,whichwascleanandsimple.ObjectA triggerseventXYZonobjectB,andthiseventcancarryapayload–thedatathatobjectBneedsto operate.Usingmodernjargonit’scalledInversionofControlorDependencyInjectiondesignpattern.What’simportantisthatobjectBdoesn’tknowaboutobjectA.Onthesamenote,ifobjectB needstoreturntheresultofsomeinternalfunctionback,itwouldbroadcastthisresult“towhomit mayconcern”bytriggeringoneofitsownevents(we’llexplainthisinthesectiononcustomevents inChapter4).Thisisloosecouplinginaction. ThementalityofmanyJavaprogrammerswasdifferent.They’dassignlowerprioritytotheuser’s windowsandspendmostoftheirtimedesigningamulti-tiersystemthatdidn’tdependonanyspecificlook-and-feelandcouldbeuniversal.MeetingswithJoewouldberarebecausetheycouldn’t createadecentprototypefast.FancyIDEswithGUIdesignerslikeMatisseweren’tinthepicture yetand,moreimportantly,Javaprogrammerswerethinkingbig:UML,designpatterns,applicationservers,andclustering.Theyenjoyedtheprogrammingprocessforitself.Theydidn’tlikethe mindsetofold-fashionedPowerBuilderorVisualBasicprogrammerswhowerethinkingwindows andscreens. WithFlexyoustartedtocareaboutthebusinessusersagain.Youcanchangetheprototypetwicea day,andJoecandothesamewithhisbusinessrequirements.Nopileofprojectdocumentationis needed.Thenapkinisbackanditworks.Flexarchitectscangivetheserver-sideJavateamthefinal okayonlyafterJoeis100%happy. Besides,withFlexwecanhavethebestofbothworlds:thesourcecodeoftheFlexframeworkis available,wecanlearnhowitworksinside,andoverridesomefunctionalitywithwhatsuitsour needsbetter. RIAWITHADOBEFLEXANDJAVA
15
CHAPTER1
Working with Flex promotes agile development, where the people we build the software for are thedrivingforce.Agiledevelopmentmethodologysuggestsminimizingeachdevelopmentcycle tomitigatetheriskofdeliveringsomethingtotheusersthattheydon’twant.Agiledevelopment encouragesfrequentface-to-facemeetingswiththeendusersasopposedtopreparingandsendingdocumentsbackandforth. ThemainpageoftheManifestoforAgileSoftwareDevelopment(http://www.agilemanifesto.org/) prioritizes: • • • •
Individualsandinteractionsoverprocessesandtools Workingsoftwareovercomprehensivedocumentation Customercollaborationovercontractnegotiation Respondingtochangeoverfollowingaplan
Inthisbookwetriedtouseagiledevelopmentmethodologies.Whenyoufollowtheexamples,doa “reverseanalysis”ofthecodeyou’vewritteninthepasttoseeifitcouldbedonesimplerusingthe techniqueswedescribe.
Summary Inthischapterwe’vepresentedyouwithaquickoverviewofmajorRIA-enablingtechnologiesand variousapproachestomakingWebapplicationsricher.It’sbynomeansacompletelist:newcommercial and open source RIA frameworks and components arise every day. However, given the overallbusinessandtechnicalvalue,there’snotasingleRIAvendorthattheauthorsofthisbook wouldputanywherenearAdobe’spositionwithFlex.AdobeFlex2isanexcellenttoolforcreating commercial-strengthrichInternetapplications.Itintegrateswithvariousbackendsandelegantly accommodatesthecomplexitiesofanenterprisetechnologysetup.Ifyou’rethinkingstrategically, ifyou’dliketokeepyourbusinesscompetitive,youneedtoinnovate.
16
RIAWITHADOBEFLEXANDJAVA
ArchitectureofRichInternetApplications
RIAWITHADOBEFLEXANDJAVA
17
18
RIAWITHADOBEFLEXANDJAVA
CHAPTER
2
GettingFamiliarwithFlex
RIAWITHADOBEFLEXANDJAVA
19
CHAPTER2
GettingFamiliarwithFlex
Even though this book is not a Flex tutorial, we need to spend some time introducing this RIA tool.WhileFlexcomeswithanIntegratedDevelopmentEnvironment(IDE)calledFlexBuilder(see Chapter3),inthischapterwe’llgentlyintroduceyoutoFlexprogrammingbywritingacoupleof simpleFlexprogramsusingonlythetoolsandcomponentsthatareavailableforfree.We’vealso includedabriefcomparisonoftheJavaandActionScriptlanguagesandsomegood-to-knowtopics:framerates,theapplicationinitializationprocess,anddisplaylists. Workingwithtexteditorsandcommand-linecompilersisnotthemostproductivewayofdevelopingwithFlex,butcommand-linecompilersdefinitelyhavetheiruse.Inatypicalreal-worldscenario, developers working on a decent-size project would use both – Flex Builder IDE to create andtesttheirapplications,andthenbuildtoolslikeAntorMaventoinvokeFlexcommand-line compilerstocreateanddeploytheapplication.ThisisespeciallyimportantbecauseFlexapplicationsareoftenusedinamixedenvironmentwhereanapplicationbuildconsistsofmultiplesteps ofcompilinganddeployingmoduleswritteninmorethanonelanguage,forexample,Flexforthe GUIpartandJavafortheserver-sidecomponents.
FreeFlex2Ingredients ThisisabriefoverviewofwhatcomprisesFlexandwhat’savailableforfree: MXML:AnXML-basedmarkuplanguagethatletsyoudefineGUIcomponentsusingdeclarative programming.YouaddtagstoyourprogramthatrepresentvisibleandinvisibleFlexobjects.For example,theMXMLtagwilldisplaythetext “Enterpassword:”in20pixelstotherightfromthetopleftcorneroftheFlashPlayer’swindowand 40pixelsdown. ActionScript3.0:Anobject-orientedlanguagewithasyntaxsimilartoJava’s.ActionScriptclasses andMXMLtagsareinterchangeable.MostActionScriptclassescanberepresentedbyMXMLtags andviceversa.EvenifyoucreateyourapplicationonlyinMXML,it’llbeconvertedtoActionScript andthencompiledintobytecodeforexecutioninFlashPlayer.
20
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Flexframework:Alibraryofextensiblecomponentslocatedinmultiplepackages.Youcancreate yourownclassesandnewMXMLtagsasneeded.JustwriteapieceofMXMLcode,saveitinafile calledMyCode.mxmlandyou’vegotanewtag:.OrcreatethenewclassMyCode.asand useiteitherwithotherActionScriptclassesorasanMXMLtag. mxmlc:AprogramthatlaunchesMXMLandtheActionScriptcompilerlocatedinaJavaarchive called mxmlc.jar.This command-line compiler generates Flex applications as .swf (pronounced swif )files. compc1:AprogramthatgeneratesFlexcomponentlibrariesas.swc(pronouncedswick)files.An swcfilecontainsanswf,catalog.xml,andassetfilesifneeded.We’lldiscusstheuseofcomponent librariesinChapter10. fdb:Acommand-lineFlexdebuggingutilitythatyoucanstartfromacommandline.Itrequiresthe debuggerversionofFlashPlayerthatcomeswiththeFlexframework. asdoc:AdocumentationtoolthatrunsthroughfilescontainingFlexsourcecodeandcreatesonlinedocumentationsimilartotheonethatcomeswiththestandardFlexAPI. FlexDataServices2Express:DeployedasastandardJavaEEapplication,itprovideshigh-performanceconnectivitywithexistingserver-sidedataandbusinesslogic.Basedonmessagingarchitecture,FlexDataServicesintegrateswithexistingcommonmiddlewareandprovidesservices thatautomaticallysynchronizedatabetweenclientandserver,supportsreal-timedatapushand publish/subscribemessaging,andfacilitatesthecreationofcollaborativeapplications. FlashPlayer9:AruntimeenvironmentforyourFlex2applications(.swfand.swcfiles).Adebug versionofFlashPlayercanoutputatracemessagetoaconsoleorintoafile.
DownloadingtheFlex2Framework AllFlex2componentscanbedownloadedfromtheURL:http://www.adobe.com/cfusion/tdrc/index.cfm?product=flex. Flex Builder IDE comes with command-line compilers and the Flex framework. But since this chapterisaboutworkingwithfreeFlexcomponents,we’llassumeforamomentthatyou’renot planningtouseFlexBuilderandwilljustgettheFlexSDKfromthesectioncalledtheFreeFlex2 SDKandLanguagePack. Afterthedownloadiscomplete,unzipthearchivefileintoaseparatedirectory.Forexample,in Windowscreateafolderc:\FreeFlexandaddc:\FreeFlex\bintothesystemvariablePATHonyour computer.Openthecommandwindowandtypemxmlc–version.Ifyoudideverythingright,your screenmaylooklikeFigure2.1:
RIAWITHADOBEFLEXANDJAVA
21
CHAPTER2
Figure2.1Checkingtheversionofmxmlccompiler
SincethemxmlccompileriswritteninJava,itusestheJavaRuntimeEnvironment(JRE)during thecompilationprocess.FlexFrameworkcomeswithitsownJRE(whenthiswaswrittenversion 1.4.2).YoucanalsodownloadamorecurrentversionofJREathttp://www.java.com/en/download/manual.jsp.HavingthelatestJREonyourPCmayincreasethespeedofthecompilation process. AfterinstallingtheFlexFramework,you’llfindtheconfigurationfilejvm.configand,ifyouhavea Windowsmachine,youcaneditthisfiletousethelatestJRE2.Ifyou’renotusingWindows,youcan modifythecompiler’sshellscripttopointittotherightJRE. Ifyoudon’thaveFlashPlayer9installed,getitfromthesameWebpage.ThedebugversionofFlash Player9islocatedunderyourFlexinstallinthedirectoryPlayer. WearenowreadytowriteourfirstFlexprogram.Guesswhatitsnamewillbe.
HelloWorldinMXML ItsaysinWikipediathat“HelloWorldisasoftwareprogramthatprintsHelloWorldonadisplay device.Itisusedinmanyintroductorytutorialsforteachingaprogramminglanguageandmany studentsuseitastheirfirstprogrammingexperienceinalanguage.”(Seehttp://en.wikipedia.org/ wiki/Hello_world.) Afterseeingthesetwomagicwords“HelloWorld”onthesystemconsoleoragraphicalwindow, you’ll get this warm feeling of knowing that your software is installed and configured properly, compilerswork,theruntimeenvironmentoperates,andyou’rereadytolearnthetool,whichin ourcaseisFlex. Ladiesandgentlemen,pleasewelcome,HelloWorldinMXML.
22
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Listing2.1HelloWorldprogram:HelloWorld.mxml Typethiscodeintoyourfavoriteplaintexteditorandsaveitinaseparatedirectory.Inacommand window,switchtothisdirectoryandcompilethisapplicationbyenteringmxmlcHelloWorld.mxml. ItshouldcreatethenewfileHelloWorld.swf. Bydefault,.swffilesareassociatedwithFlashPlayer–justdouble-clickonHelloWorld.swfinWindowsExplorerandit’llrunourHelloWorldprogram:
Figure2.2HelloWorldinFlashPlayer
TheMXMLfilehastranslatedHelloWorldintoActionScriptandcompileditintothe.swffile.Duringthefirstpass,thenameofyourMXMLfilebecomesthenameofthemaingeneratedActionScriptclass,forexample,withthevirtualwaveofamagicwandtheMXMLtag turnsintotheActionScriptcode: class HelloWorld extends Application {…}
AllotherMXMLtagswillundergoasimilarprocedure.Forexample,themarkupcode
leadstothegenerationofaninstanceoftheActionScriptclassLabelandcallsitssetterfunctionto assignthetext“HelloWorld”toanattributecalledtext.
Typically,youdeployaswffileonyourWebserverasapartofanHTMLpage3.Ifyou’reusinga command-lineFlexcompiler,you’llhavetocreatesuchanHTMLfilemanually,asopposedtoFlex Builder, which automatically creates these wrappers for you. But even the process of manually RIAWITHADOBEFLEXANDJAVA
23
CHAPTER2
creatingHTMLwrappersisgreatlysimplifiedbyusingtheso-calledHTMLtemplatesthatcome withFlexlocatedinthefolderresources.Currently,therearesixHTMLtemplatesofvariousflavors thatcandetecttheclient’sversionofFlashPlayer,offeranexpressinstalloftheplayer,andsupport historymanagement.TheprocessofcreatingHTMLwrappersisdescribedintheproductmanual, “BuildingandDeployingFlex2Applications.”
SpecifyingCompilerOptions TheFlexcompilercomeswithanumberofusefuloptions.Forexample,youcanrequestthatthe generatedActionScriptcodenotbedeletedaftercompilation.Toseewhichcompileroptionsare available,typethefollowingatthecommandprompt: mxmlc –help list advanced
Oneoptioniskeep-generated-actionscript.Let’srecompileHelloWorldwiththisoption: mxmlc -keep-generated-actionscript HelloWorld.mxml
NowtheFlexcompilerwillnotonlyproducethe.swffile,butwillalsocreateasubdirecorycalled generatedandyouwillbeabletobrowsethesourcecodeofmorethantwo-dozenActionScript HelloWorld-supportingfiles. The speed of the command-line compilation may be improved if you use the option incremental=true(inFlexBuilderthisoptionisonbydefault). YoucanreadmoreaboutFlexcompileroptionsinthesection“UsingtheFlexCompilers”inthe productmanual“BuildingandDeployingFlex2Applications.” Youcanalsospecifycompileroptionsinaspecialconfigurationfilenamedflex-config.xmlorin acustomconfigurationfilewhosenameshouldbespecifiedintheload-configcompiler’soption. Justkeepinmindthatthecompilationoptionsfromflex-config.xmlfileaffectsallyourprojects. AnotherwayofspecifyingsomeFlexcompileroptionsfromanActionScriptclass,namelywidth, height,frameRate,andbackgroundColor,isthroughaspecialmetatagSWFthatyouputabovethe classdeclarationline.Forexample: [SWF(frameRate=”50”, backgroundColor=”#FFFFFF”)] public class SomeApp { }
If compilation fails, you’ll see a short error description, but if you’re still not sure what this error means, consult http://livedocs.adobe.com/labs/as3preview/langref/compilerErrors.html for amoredetaileddescriptionofthecompilationerrors.ThisWebpagealsohasalinktoapagedescribingruntimeerrors.
24
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
BuildingandDeployingApplicationswithAnt UnlessyourprojectisassimpleastheHelloWorldapplication,compilationanddeploymenthas tobeautomated.Creatingabuildscriptforanydecent-sizeapplication(i.e.,hundredsoffilesin Flexandserver-sideJava)isaprojectonitsown.Javaprogrammerstypicallyuseanopensource buildtoolsuchasAntorMaventoformallydescribeandexecuteallthestepsrequiredtobuildand deploytheirprojects. Ifyouhaven’thadachancetouseanyofthebuildtoolsbefore,thissectionisforyou.ThisisaminiprimeronhowtouseAnt,thepopularJava-basedbuildtoolfromApache. EvendeployingsuchasimpleapplicationasHelloWorldconsistsofseveralsteps.Forexample: 1. 2. 3. 4.
UsemxmlctocompileHelloWorld.mxmlintoaswffile. CreateanHTMLwrapperfile(s)fromoneoftheHTMLtemplates. CreateorcleanthedirectoryinyourWebserverwhereHelloWorldfileswillbedeployed. CopyorftpHelloWorld.swfandsupportingfilestothisdirectory.
Now imagine that your HelloWorld application is part of a larger application that includes Java modules that also have to be compiled and deployed in some server location.The deployment ofevenasmallprojectusuallyinvolvesatleastadozendependenttasksthathavetoexecuteina particularorder.Creatingandmaintainingabuildscriptiswellworththeeffort.
BuildingHelloWorldwithAnt WithAnt,youdescribeyourprojectinabuildfilewritteninXML(it’stypicallycalledbuild.xml).An Antprojectconsistsoftargets(i.e.,compile)that,inturn,consistoftasks(i.e.,mkdir,exec).Listing 2.2showsabuild.xmlthatwillcreateanoutputdirectorybinandcompileourHelloWorldapplicationintothisdirectory.ThisAntbuildfilehastwotargets:initandcompile.
RIAWITHADOBEFLEXANDJAVA
25
CHAPTER2
Listing2.2Antproject-build.xml Theinittargetconsistsoftwotasks:deleteandcreatethedirectorycalledbin. ThecompiletargetwillcompileHelloWorld.mxml,producingbin\HelloWorld.swf.Ifwehaveto writeacommandmanuallythatcompilesHelloWorld.mxmlintothebindirectory,itwouldlook likethis: mxmlc -output bin/HelloWorld.swf HelloWorld.mxml
Besidestargetsandtasks,ourbuild.xmlcontainstwopropertytags.Oneofthemdefinesaproperty calleddest.dirthathasavaluebinthatisthenameoftheoutputdirectoryforourcompiledHelloWorld.swf:
ThismeansthatwhereverAntencountersdest.dirsurroundedwith${and},it’llsubstituteitwith thevalueofthisproperty.Inourcasethisisbin. TheprojectinListing2.2alsodefinesapropertycalledflex.mxmlcthattellsAntwherethemxmlc compilerislocated.ThetargetcompileusesAnt’staskexec,whichwillrunthemxmlccompiler, passingittheargumentsspecifiedinthetags. Let’sdownloadAnt4fromhttp://ant.apache.org/.Installationissimple:justunzipthearchiveand addthepathtoitsbindirectory(i.e.C:\apache-ant-1.6.5\bin)totheenvironmentvariablepathof youroperatingsystem. Nowwecanopenacommandwindow,getintothedirectorywhereHelloWorld.mxmlandbuild. xmlarelocated,andenterthecommandantthatwillstartexecutingthedefaulttargetofourproject,whichiscompile.Butsincecompiletargetismarkedasadependentoninit,Antwillexecute theinittargetfirst. Figure2.3showshowtheWindowsXPcommandwindowmaylookafterrunningthisproject:
26
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Figure2.3RunninganAntBuild
Anthasanextensivesetofpredefinedtaskslikecopy,zip,jar,War,FTP,Mail,Condition,andmany moredescribedathttp://ant.apache.org/manual/. Forexample,tomakeourbuildfileworkinbothWindowsandUnixenvironments,wecanspecify thelocationofthemxmlccompilerasfollows:
YoucanpassvaluesofthepropertiesfromtheAntcommandlineusingthe–Dpropery=valueoption.Forexample,ifweusethecodesnippetabove,thepropertysdkdircanbepassedfromacommandline: ant –Dsdkdir=/opt/usr/freeflex
YoucanwritemoresophisticatedAntscripts.Forexample,youcanautomatethecreationofan HTMLwrapperforswffileswithareplaceregextaskappliedtooneoftheHTMLtemplatesprovided.YoucanwritebuildscriptsthatwillmergeFlex-generatedartifactswithJavaWebapplications. AutomatingbuildswithAntcangetyouprettyfar. TherearelotsofonlinearticlesandbooksonAnt,butdon’tmissAntinAngerbySteveLoughran inwhichhediscussessomeAntcorepractices,namingconventions,andteamdevelopmentprocesses.(Seehttp://ant.apache.org/ant_in_anger.html.)
FrameRate Flash Player renders its GUI objects (display list) at a specified frame rate. As opposed to Flash moviesthatconsistofmultipleframesdisplayedoveratimeline,Flexapplicationsdon’thavemulRIAWITHADOBEFLEXANDJAVA
27
CHAPTER2
tipleframes,buttheframerateplaysanimportantroleinFlexprograms.Thedisplaylist(yourGUI components)isrenderedatthespecifiedframerateandapplicationswithhigherframeratescan haveabetterperformingGUI,butitdoesn’tcomefree. TheFlexcompilerbuildsanswffilewithadefaultframerateof24framesasecond,unlessanother valueisspecifiedduringcompilation.Forexample: mxmlc –default-frame-rate 50 HelloWorld.mxml
This command will build an swf file with the frame rate of 50 frames a second.The display list renderingandActionScriptexecutiontaketurns,hencetheframeratecanaffectyourapplication performance. Settingtheframerateto50doesn’tmeanthateachframewillbedisplayedinexactly20milliseconds(1,000dividedby50),sincethereissomeOS/browseroverhead.Besides,somebrowsersmay imposerestrictionsonpluginstolowerCPUutilizationontheuser’smachine. Toseehowthecompiletimesettingofthedefault-frame-rateoptionaffectsprogramexecution, considerthefollowingprogramthatoneachenterFrameeventmakesasimplecalculationofthe actualframerate.
Listing2.3Testingframerate WeranthisprogramintheInternetExplorerandFirefoxbrowsersonaWindowsXPlaptopwith asingle1.8GHzCPU.Tables2.1and2.2showthedifferencebetweenrequestedandactualframe ratesandhowhardtheCPUworksbasedontheframeratesettings.
28
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Default-frame-rate
10
24
50
100
Actualframerate
9-10
18-26
35-52
50-110
CPUutilization
15-17%
15-20%
18-20%
26-28%
Table2.1FramerateswithInternetExplorer6 Default-frame-rate
10
24
50
100
Actualframerate
9-10
16-22
41-50
70-100
CPUutilization
16-20%
18-20%
22-25%
30-33%
Table2.2FramerateswithFirefox1.5
Whileresultsvaryslightly,theyareself-explanatory–lowerframeratestranslateintolowerCPU utilization.Youshouldexperimentwithframeratesinyourapplicationtofindtherightbalance betweenGUIperformanceandCPUuse.Rememberthatyourusersmayberunningseveralprogramsatthesametime,andyoudon’twanttobringtheirCPUstotheirkneesjustbecauseyou’ve enjoyedsuper-smoothgraphicsrendering.Ifyou’renotcreatingamovie,keeptheframerateas lowasyoucan.
NamespacesinMXML InXML,namespacesareusedtoavoidpotentialnamingconflictswithothercomponentswiththe samenames.Sofarwe’veonlyseenthexmlnspropertyinourMXMLsampleprograms:
The namespace mx:xmlns refers to the URI http://www.adobe.com/2006/mxml that lists valid MXMLtags.Openthefileflex-config.xmlandyou’llfindanXMLelementtherethatlinksthisURI tothefilemxml-manifest.mxlthatlistsalltheMXMLcomponents.Here’sanextractfromthismanifestfile:
IfyouwanttouseoneofthestandardMXMLcomponents,specifytheprefixmxforeachofthem. Forexample,touseanMXMLLabel,youwritethefollowing:
RIAWITHADOBEFLEXANDJAVA
29
CHAPTER2
Laterinthisbook,we’llshowyouhowtocreatecustomcomponentsandkeeptheminaseparate namespacetoavoidnamingconflicts.We’llintroduceanothernamespacewiththeURIcom.theriabook.controls.*.Forexample,inChapter9we’llbedevelopingacustomTreecomponentand Listing9.10includesthefollowingMXMLcode:
…
Thissampledefinestwonamespaces:mxandlib.Thenotationmeansthatwe’replanningtouseaTreecomponentfromthenamespacecalledlib.Asyoucanguess,we’regoingtoprogramthisTreecomponentintheActionScript’spackagecom.theriabook.controlsandwillhaveto provideeitherthecodeofourTreecomponentortheSWClibrarythatincludesthisTree. The namespace URI tells Flex where to look for the file implementing this component.You can either create a com/theriabook/controls subdirectory in the application’s directory or – preferably–keepitinaseparatelocationincludedintheclasspathofyourapplication(readaboutthe flex-config.xmlfileandthesource-pathtagintheFlexdocumentation).Sincewe’vedefinedtwo namespaceshere,wecanusecomponentsavailableinanyofthem. Youcanalsospecifyaso-calledlocalnamespaceusingnotationslikexmlns=”*”orxmlns:mylocal=”*”thattellsFlextolookforcomponentsthatarelocatedinthesamedirectoryastheMXML fileor,incaseofFlexDataServices,inthe/WEB-INF/flex/user-classesdirectory. Chapter4includesasectionthatshowshowtousenamespacesinActionScript.
FromHelloWorldtoaCalculator The HelloWorld application uses just a Label tag put directly inside the parent application tag. ButtypicalMXMLcodehasahierarchicalstructure.Theapplicationmayincludecontainersthat inturncanconsistofothercontainersorindividualcomponents.Forexample,acalculatorcan consistoftwopanels:thetoponewillbeusedfordisplayingresultsandthebottomonewillhold thebuttonswithdigits.We’llputbothofthesepanelsinsidethetagVBox–theverticalbox(Java programmerscanthinkofitasaSwingBoxLayout).
30
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Listing2.4Calculator1.mxml-AVBoxwithtwopanels AftersavingthecodeaboveinthefileCalculator1.mxml,we’llcompileitwithmxmlcandrunitin FlashPlayer:
Figure2.4RunningCalculator1.mxml inFlashPlayer
Thecalculator’spanelsarereadyandafteraddingatexttagtothetoppanelandseveralbuttontags tothebottomone,thecodewilllookslikethis:
RIAWITHADOBEFLEXANDJAVA
31
CHAPTER2
Listing2.5Calculator2.mxml–Addingcontrols Compileandrunthiscodeandyou’llseethefollowingscreen:
Figure2.5RunningCalculator2.mxml inFlashPlayer
It’s not the prettiest calculator you’ve ever seen, but it’s not bad for two minutes of coding.The structureoftheprograminListing2.5issimple,readable,andelegant. ThepanelscanincludeeitherstandardFlexcontainersorcustomcomponentsthatyouorother developerscreated.Table2.3providesthewidgetsthatcomewiththeFlex2framework.
32
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Layouts
ApplicationControlBar,Canvas,ControlBar,Form,FormHeading,FormItem,Grid,HBox, HDividedBox,HRule,Panel,Spacer,Tile,TitleWindow,VBox,VDividedBox,VRule
Navigators
Accordion,ButtonBar,LinkBar,MenuBar,TabBar,TabNavigator,ToggleButtonBar,ViewStack
Controls
Button,CheckBox,ColorPicker,ComboBox,DataGrid,DateChooser,DateField,HSlider, HorizontalList,Image,Label,Link,List,Loader,NumericStepper,PopUpButton,ProgressBar, RadioButton,RadioButtonGroup,RichTextEditor,Text,TextArea,TextInput,TileList,Tree, VSlider,VideoDisplay
Table2.3Flexwidgets Youcancreatecustomcomponentsbycombiningthesewidgets.Forexample,youcancreatea customlogincomponentwithtwoLabelcontrolsforIDandpassword,twoTextInputs,twoButtons,andaLinkforthe“ForgotPassword?”function.Suchcustomcomponentscanbereusedby multipleapplications.StartinginChapter6we’llshowyouhowtocreatecustomcontrols.
AddingSomeActionScript MXMLtakescareoftheGUI,butwhere’stheprocessinglogic?Whatwillhappenwhenauserpressesabuttononourcalculator?Nothingwillhappenatthispoint.Theprocessinglogichastobe writtenusingActionScript3,whichisanobject-orientedlanguagewithasyntaxsimilartoJava’s, andbothoftheselanguagescanbeintegratedquitenicelyindistributedInternetapplications. ActionScriptwasfirstintroducedinAugustof2000asaprogramminglanguageforFlashPlayer5. ActionScript2cameintothepictureinSeptemberof2003withFlashPlayer7.ActionScript3(June 2006)isafullrewriteofthislanguagethatcanbeusedforcreatingapplications(evenwithoutusing MXML)forFlashPlayer9.ActionScript3isbasedonthethirdeditionofECMAScript,whichisan internationalstandardizedprogramminglanguageforscripting. FlexFrameworkiswritteninActionScript3.Readersfamiliarwithpreviousversionsofthislanguagemaywanttocheckout“ActionScript3:LearningTips”athttp://www.adobe.com/devnet/actionscript/articles/actionscript_tips.html. We’llbeusingActionScript3ineverychapterofthisbook,butfordetailedcoverageofthislanguagepleaseconsulttheproductmanual“ProgrammingActionScript3.0”availablefordownloadatwww.flex.org. InFlex,youlinkMXMLcomponentswiththeActionScriptprocessinglogicusingoneofthefollowingmethods: • •
EmbedActionScriptcodeinmxmlfilesusingthetag IncludeexternalActionScriptfilesintomxml,forexample:
RIAWITHADOBEFLEXANDJAVA
33
CHAPTER2
• •
ImportActionScriptclasses ImportActionScriptcompiledcomponents
AtypicalFlexprojectconsistsof.mxmlfileswithdeclaredcomponentssincethefilescontaining ActionScriptcode,.swcfileswithcompiledcomponents,.cssfileswithpresentationstyleinfo,and otherassetslikeimagefiles,ifneeded.Allofthesecomponentscanbecompiledintoone.swffile, oranumberofswclibraries,andrunbyFlashPlayer9.OnlyoneMXMLfileshouldhavethetag;therestoftheMXMLfilesusuallystartwithothertags.
TheFirstDatewithEvents Calculator2displaysGUIcomponentsusingMXML.Fortheprocessinglogicwe’lluseActionScript, andtheonlymissinglinkishowthesecomponentscaninitiatethisprocessinglogic.InFlexthisis doneusingevents.Forexample:
ThedisplayDigit()isafunctionwritteninActionScriptthatcancontainsomeprocessinglogicand displaysthedigit.Chapter4willoffermoredetailsonusingstandardandcustomevents. Event-drivenprogrammingisaboutidentifyingimportanteventsthatmayhappentoyourapplicationandwritingeventhandlerstoprocesstheseevents.Ifaparticulareventneverhappened,the codefromthecorrespondingeventhandler(akalistener)isn’tcalled. Notalleventsaretriggeredbytheuser’sactions.Somesystemeventsmaybetriggeredbyyourapplicationcodeorotherprocessessuchasthearrivalofthedatabasequeryresultsetorthereturnof theWebServicescall.YourapplicationscandefineitsowneventsandtriggerthemfromMXMLor ActionScriptcode.Writingeventlistenersthatcontaincode-handlingeventsisautomatedinFlex, butyouhaveameansofwritingsuchlistenersonyourActionScriptcode. Forexample,addingasimpleMXMLattributetoprocessamouseOverevent
requiresthecompilertoworkharderasnowitneedstodothefollowing: • • •
GenerateaneventlistenerfortheLabelobject CreateanobjecttoprocessthemouseOvereventintheuser-definedfunctionchangeTextColor() WirethislistenerwiththeinstanceoftheLabel
Javacompilersworkfasterbecausetheirjobisaloteasier–aJavaprogrammercreatesallthese instancesandlistenersmanuallysothejavaccompilerspendsmostofitstimemerelytranslating theJavasourcecodeintobytecode.
34
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Thethirdversionofthecalculatoraddsseveralsamplesofprocessingthebutton-clickevent.In Listing2.6theb1buttonusesinlineActionScripttoconcatenatedigit1tothetextfieldresult: click=”result.text+=’1’”
Aclickonbutton2callsanActionScriptdisplayDigit()functiondefinedinthesection. Aclickonbutton3concatenatesthelabelofthebuttontotheresultfield. Inthefourthbutton,wejustfoolaroundtoillustrateusingmorethanoneActionScriptinlinestatements–firstreplacethelabelofthebuttonto9andthendisplayit.Eventhoughit’snotrecommended,youcanusemulti-lineinlineActionScriptcodebysurroundingitwithcurlybraces.
Listing2.6Calculator3.mxml–Addingeventprocessing
RIAWITHADOBEFLEXANDJAVA
35
CHAPTER2
Aclickonthefifthbuttonshowshowtodisplayamessagebox,andFigure2.6depictsasnapshot ofCalculator3’sprogramafterpressingthefirstfivebuttons.
Figure2.6:Calculator3afterpressing1,2,3,4,and5
WhyActionScript3.0? Ifyouhaveanyexperiencewithotherprogramminglanguages,thenaturalquestioniswhywould youstartprogrammingWebapplicationsinActionScript3asopposedtoJavaorJavaScript? Thesimpleansweristhatanobject-orientedlanguagelikeActionScript3combinedwithMXML makes GUI programming easier. Java Enterprise Edition (Java EE) with its multi-threading features,plusreliableandscalableapplicationservers,isagreatchoiceforserver-sideapplications. JavaSwing,thelibraryforGUIdevelopmentispowerfulbutcomplicated.Dealingwithevent-dispatchthreadandsynchronizationissues,lackofdatabinding,andtheneedtoprogrammultiple eventlistenersmanuallymakesprogramminginJavaSwingalotlessproductive. ActionScriptusesasingle-threadedmodel(atleastfromthedeveloper’sperspective)andavoids manyoftheJavaSwingissuesbymakingallclient/servercommunicationsasynchronous.Aclient programsendsarequesttotheserverspecifyingthenameofthecallbackhandler,andtheusercan
36
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
immediatelycontinueworkingwiththescreen,whichnever“freezes.”Whentheresultcomesback, thishandlerwillprocessit. Oneofthewaystocategorizeprogramminglanguagesisbyhowstricttheyarewithvariabletypes: somelanguagesusestaticandothersdynamicdatatyping.Instaticallytypedlanguages(Java,C++, C#)youmustdeclareavariableofaparticulartype,thecompilerwillensurethatyourprogram usesthisvariableproperly,andyoushouldnotexpectanydatatype-relatedsurprisesatruntime. Indynamiclanguages(Perl,Python,ECMAScript,JavaScript)youdon’thavetodeclarethevariablesupfront. ActionScriptoffersyouabalancebetweenstaticanddynamiclanguagesbyallowingyoutoprograminbothJavaandJavaScriptstyles.Butthefreedomofnotdeclaringvariablescomeswitha pricetag:anyruntimeenvironmentcanmoreefficientlyallocaterequiredresourcesifthetypesof programelementsareknowninadvance.ActionScript3givesyouthechoiceofwritinghighlyefficient,staticallytypedcode,highlydynamicandlooselytypedcode,oracombinationofthetwo. ActionScript3isacompiledobject-orientedlanguageanditsperformancehasbeensubstantially improvedcomparedtopreviousversionsofthelanguage.Itscodeiscompiledintobytecodeand runsintheActionScriptVirtualMachine(AVM)inFlashPlayer9.Strictlyspeaking,FlashPlayer 9includestwoAVMs:AVM1executesthecodewrittenintheolderversionofthelanguage,while AVM2runsthecodeproducedbyActionScript3.HavingtwoVMsinFlashPlayerhasincreasedthe sizeofFlashPlayertoabout1.2MB,butitensuresthatexistingapplicationswon’tbreakandusers won’tneedtokeeptheolderversionofFlashPlayer. InAVM2,mostofthecodegoesthroughaJust-in-Timecompilerthatconvertsthebytecodeinto machinecodeandsubstantiallyimprovesperformance. AnothernotablefeatureofAS3(andMXML)istheirsupportofECMAScriptforXML(E4X),which makesworkingwithXMLabreeze.You’llseemanyexamplesofE4Xusageinthisbookstartingwith Chapter4.
ComparingActionScript3.0andJava5 We can’t provide a detailed description of either language in this book, but a short comparison tableofthemajorelements/conceptsofthesetwolanguagesmayserveasaquickreference.You canreadTable2.4eitherleft-to-rightorright-to-leftdependingonwhatyourprimaryprogramminglanguageis. Concept/Language Construct
Java5.0
ActionScript3.0
Classlibrarypackaging
.jar
.swc
Inheritance
ClassEmployeeextends Person{…}
ClassEmployeeextendsPerson{…}
RIAWITHADOBEFLEXANDJAVA
37
CHAPTER2
Variabledeclaration& initialization
StringfirstName=”John”; DateshipDate=newDate(); inti; inta,b=10; doublesalary;
VarfirstName:String=”John”; varshipDate:Date=newDate(); vari:int; vara:int,b:int=10; varsalary:Number;
Undeclaredvariables
n/a
It’stheequivalentofwildcard-typenotation*. Ifyoudeclareavariablebutdon’tspecifyits type,the*typewillapply Adefaultvalueis:undefinedvarmyVar:*;
Variablescopes
block:declaredincurly braces,local:declaredina methodorblock
Noblockscope:theminimalscopeisafunction
member:declaredonthe classlevel
member:declaredontheclasslevel
noglobalvariables
local:declaredwithinafunction
Ifavariableisdeclaredoutsideofanyfunction orclassdefinition,ithasaglobalscope.
Strings
Immutable,storesequences Immutable,storesequencesoftwo-byteUnioftwo-byteUnicodecharcodecharacters acters
Terminatingstatements withsemicolons
Amust
Ifyouwriteonestatementperlineyoucanomit it
Strictequalityoperator n/a (comparingobjectswithout thetypeconversion)
=== forstrictnon-equalityuse !==
Constantqualifier
Thekeywordfinal
Thekeywordconst
finalintSTATE=”NY”;
constSTATE:int=”NY”;
Typechecking
Static(checkedatcompile time)
Dynamic(checkedatruntime)andstatic(it’s so-called‘strictmode,’whichisthedefaultin FlexBuilder)
Typecheckoperator
instanceof
is–checksdatatype,i.e.,if(myVaris String){…} Theisoperatorisareplacementforolder instanceof
Theasoperator
n/a
Similartoisoperator,butreturnsnotBoolean, buttheresultofexpression: varorderId:String=”123”; varorderIdN:Number=orderIdasNumber; trace(orderIdN);//prints123
38
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
Primitives
byte,int,long,float, double,short,boolean,char
allprimitivesinActionScriptareobjects. Boolean,int,uint,Number,String Thefollowinglinesareequivalent; varage:int=25; varage:int=newint(25);
Complextypes
n/a
Array,Date,Error,Function,RegExp,XML,and XMLList
Arraydeclarationand instantiation
intquarterResults[]; quarterResults= newint[4];
ArraysinActionScriptresembleJavaArrayList:
intquarterResults[]={25,33,56,84};
varquarterResults:Array =newArray(); or varquarterResults:Array=[]; varquarterResults:Array= [25,33,56,84]; AS3alsohasassociativearraysthatusenamed elementsinsteadofnumericindexes(similarto Hashtable).
Thetopclassintheinheritancetree
Object
Object
Castingsyntax:castthe classObjecttoPerson:
Personp=(Person) myObject;
varp:Person=Person(myObject); or varp:Person=myObjectasPerson;
Upcasting
classXyzextendsAbc{} AbcmyObj=newXyz();
classXyzextendsAbc{} varmyObj:Abc=newXyz();
Un-typedvariable
n/a
varmyObject:* varmyObject:
Packages
packagecom.xyz; classmyClass{…}
packagecom.xyz{ classmyClass{…} } ActionScriptpackagescanincludenotonly classes,butseparatefunctionsaswell
Classaccesslevels
public,private,protected ifnoneisspecified,classes havepackageaccesslevel
public,private,protected ifnoneisspecified,classeshaveaninternal accesslevel(similartothepackageaccess levelinJava)
RIAWITHADOBEFLEXANDJAVA
39
CHAPTER2
Customaccesslevels: namespaces
n/a
SimilartoXMLnamespaces. namespaceabc; abcfunctionmyCalc(){} or abc::myCalc(){} usenamespaceabc;
Consoleoutput
System.out.println();
//indebugmodeonly trace();
Imports
importcom.abc.*; importcom.abc.MyClass;
importcom.abc.*; importcom.abc.MyClass; packagesmustbeimportedeveniftheclass namesarefullyqualifiedinthecode.
Unorderedkey-valuepairs
Hashtable,Map
AssociativeArrays
Hashtablefriends=new Hashtable();
Allowsreferencingitselementsbynames insteadofindexes. varfriends:Array=newArray(); friends[“good”]=”Mary”; friends[“best”]=”Bill”; friends[“bad”]=”Masha”;
friends.put(“good”, “Mary”); friends.put(“best”, “Bill”); friends.put(“bad”, “Masha”); StringbestFriend=friends. get(“best”); //bestFriendisBill
40
varbestFriend:String=friends[“best”]; friends.best=”Alex”; Anothersyntax: varcar:Object={make:”Toyota”, model:”Camry”}; trace(car[“make”],car.model); //Output:ToyotaCamry
Hoisting
n/a
Thecompilermovesallvariabledeclarations tothetopofthefunctionsoyoucanusea variablenameevenbeforeit’sbeenexplicitly declaredinthecode
Instantiationobjectsfrom classes
Customercmr=newCustomer();
varcmr:Customer=newCustomer();
Classcls=Class. forName(“Customer”); ObjectmyObj= cls.newInstance();
varcls:Class=flash.util.getClassByName(“Cu stomer”); varmyObj:Object=newcls();
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex Privateclasses
privateclassmyClass{…}
TherearenoprivateclassesinAS3.
Privateconstructors
Supported.Typicaluse: singletonclasses
Notavailable.Theimplementationofprivate constructorsispostponedsincetheyaren’tpart oftheECMAScriptstandardyet. TocreateaSingletonusethepublicstatic methodgetInstance().Inthepublicconstructor checkiftheinstancealreadyexists,throwan error.
Classandfilenames
Afilecanhavemultiple classdeclarations,butonly oneofthemcanbepublic, andthefilemusthavethe samenameasthisclass.
Afilecanhavemultipleclassdeclarations,but onlyoneofthemcanbeputinsidethepackage declaration,andthefilemusthavethesame nameasthisclass.
Whatcanbeputina package
Classesandinterfaces
Classes,interfaces,variables,functions, namespaces,andexecutablestatements
n/a Dynamicclasses(define anobjectthatcanbealteredatruntimebyadding orchangingpropertiesand methods).
dynamicclassPerson{ varname:String; } //Dynamicallyaddavariable//andafunction Personp=newPerson(); p.name=”Joe”; p.age=25; p.printMe=function(){ trace(p.name,p.age); } p.printMe();//Joe25
Functionclosures
n/a.Closuressupportisa proposedadditiontoJava7
myButton.addEventListener(“click,”myMethod); Aclosureisanobjectthatrepresentsa snapshotofafunctionwithitslexicalcontext (variable’svalues,objectsinthescope).Afunctionclosurecanbepassedasanargumentand executedwithoutbeingapartofanyobject
Abstractclasses
Supported
Thereisnomechanismtomarkclassmethods withtheabstractkeywordthatwouldenforceimplementationofthesemethodsinthe descendents
Functionoverriding
Supported
Supported.Youmustusetheoverridequalifier
RIAWITHADOBEFLEXANDJAVA
41
CHAPTER2
Method/constructoroverloading
Supported
Notsupported.Youcanuseaworkaroundwith …restparameter: publicfunctionMyCLass(…args){ switch(args.length){ case0:constructor1();return; case1:constructor2(args[0]); return; case2:constructor3(args[0], args[1]);return; … }} Thissamplecoversthecaseofconstructors havingadifferentnumberofparameters.To makeitworkwithfunctionshavingthesame numberofparametersbutdifferenttypes,you’d havetoaddthetypechecktoeachofthecases above,i.e.,if(args[0]isString){ //doonething }elseif(args[0]isNumber){ //doanotherthing }
Interfaces
Exceptionhandling
classAimplementsB{…}
classAimplementsB{…}
interfacescancontain methoddeclarationsand finalvariables
interfacescancontainonlyfunctiondeclarations
Keywords:try,catch,throw, Keywords:try,catch,throw,finally finally,throws Amethoddoesn’thavetodeclareexceptions. Uncaughtexceptionsare CanthrownotonlyErrorobjects,butalso propagatedtothecalling numbers: method throw25.3; FlashPlayerterminatesthescriptincaseof uncaughtexception
Regularexpressions
Supported
Supported
Table2.4Java/ActionScriptcheatsheet
FlexFrameworkAPIDocumentation FlexFrameworkcomeswithafullydocumentedAPIinaformatsimilartoJava’sjavadoc.WerecommendyoudownloadandinstalltheFlexFrameworkdocumentationandcreateabookmarkin yourWebbrowsertohaveithandy.Forexample,ifyou’veunzippedthedocumentfilesinthedirec-
42
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
toryC:\Flex2\doc\flex2docs\langref,bookmarkthefollowingURI:file:///C:/Flex2/doc/flex2docs/ langref/index.html. TheWebbrowser’sscreenwilllooklikeFigure2.7.
Figure2.7DocumentationontheFlex2API
Tocreatesimilarlylookingdocumentationforyourownclassesandcomponents,gettheASDoc tool at: http://labs.adobe.com/wiki/index.php/ASDoc:Using_ASDoc. For more advanced documentingfunctionality,checktheFlex2Docutilityatmyflex.org.
SeparatingDesignandDevelopment We’vealreadylearnedthatatypicalFlexprogramconsistsofMXML,amarkuplanguageforcreatingaGUI,andtheprogramminglanguageActionScript.Thisopensupanopportunitytoseparate design and development responsibilities between different people, at least in enterprise project development.Mid-sizeandlargeorganizationscanhaveagroupofUIdesignersthatknowhowto prototypegood-lookingandintuitiveWebsites. Inaperfectworld,professionaldesignerswouldcreatesuchprototypesusingMXML;professional softwaredeveloperswouldjustwriteprogramsinActionScript.InrealitythoughyourMXMLfiles willhavesomeamountofActionScript.Trytokeepittoaminimumbutdon’tkillyourselftryingto achievetheperfectseparationofresponsibilities. RIAWITHADOBEFLEXANDJAVA
43
CHAPTER2
Besides MXML,Web designers will find themselves at home with Cascading Style Sheets (CSS), which in Flex play the same role and have the same syntax as CSS in HTML.Your firm-specific fonts,coloring,andotherstylesshouldbedonewithCSS. ThereisaslightdifferenceinCSSuse:inHTMLpages,youcanchangetheCSSfile,presstheRefreshbuttoninyourbrowser,andtheappearanceoftheWebpagewillchangeaccordingly.InFlex, if you assign CSS statically either by providing the name of a .css file or by using an object,theyareembeddedintoSWF,soifyoudecidetochangethelookofyourGUIcomponents, youhavetomodifytheCSSfileandrecompiletheapplication. YoucanalsocreateandassignstylestoFlexcomponentsdynamicallyfromwithinyourActionScriptcodebycreatinganinstanceoftheStyleSheetobject,assigningvaluestoitsproperties,and thencallingthesetStylemethodontherequiredUIcomponent. FlexStyleExplorerisahandytoolthatautomatestheprocessofwritingCSS(seehttp://examples. adobe.com/flex2/consulting/styleexplorer/Flex2StyleExplorer.html). Select the kind of componentorcontainerthatyouwanttomake“stylish,”pickapplicableparametersontheleft,seehow itlooksinthemiddle,andcopytheCSSfromtherightintoyourtag:
44
Figure2.8Flex-styleExplorer
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
WorkingwithDisplayObjectsinActionScript FlashPlayershowsyourapplication’sobjectsinadisplayareathatisknownas“stage”andisrepresentedbytheclassStage.Whenaprogramrendersdisplayobjectstothestage,FlashPlayeruses adisplaylisttomanagethem.Inotherwords,yourActionScriptprogramhastoaddrequiredsubclassesoftheDisplayObjecttooneofthecontainerssotheycanberenderedanddisplayed.Since theDisplayObjectclassisinheritedfromEventDispatcher,allGUIcomponentsarecapableofprocessingevents. EachFlexapplicationhasatleastonedefaultcontainerthatisrepresentedbyaclassApplication thatcorrespondstothemxmltag. Ifyou’regoingtoprograminActionScript,youshouldbeawareofthefactthatyoucan’tinstantiate DisplayObject,butyoucancreateinstancesofitssubclasses(Shape,SimpleButton,andothers) andaddthemtothedisplaylist.FlashPlayerrenderschangestoDisplayObjecttothestageforeach videoframe(alsocalledkeyframe). YoucanaddseveraldisplayobjectstoadisplaylistofoneofthesubclassesofDisplayObjectContainer.Forexample,theclassSpriteisadirectdescendantofDisplayObjectContainer,andflash. display.MovieClipisasubclassofSprite. While MovieClip is a suitable class for Flash programs that display a sequence of frames over a timeline,forbusinessapplicationsit’stheSpriteclass,alightercontainerthatdoesn’tincludethe timelinesupport.InActionScript,Spriterepresentsadisplayobjectinadisplaylistnode.Asan example,youcancreateanActionScriptclassthatextendsSprite,andaddoneormoredisplayobjectstoyourclasswithaseriesofthemethodcalledaddChild().FlashPlayerfindsclassesthrough therootofthedisplaylist.OnlyaftercallingaddChild()willacomponentbeaddedtothedisplay listandtheSystemManagerassignedtoit.
TheApplicationLoadingProcess Flexdefinesanumberofso-calledmanagerclasseslocatedinthepackagemx.managers.These classescontrolthemousecursor,drag-and-dropoperations,history(similartotheWebbrowsers’ Back/Forwardbuttons),pop-upwindows,tooltips,andotherthings. WhenFlashPlayerloadsandstartsyourFlexapplication,theSystemManagercontrolstheapplicationwindow;createsandparentstheApplicationinstance,pop-ups,andcursors;andmanagesthe classesintheApplicationDomain.TheSystemManageristhefirstclassinstantiatedbyFlashPlayer foryourapplication.Itstoresthesizeandpositionofthemainapplicationwindowandkeepstrack ofitschildren,suchasfloatingpop-upsandmodalwindows.UsingSystemManageryoucanaccess embeddedfonts,styles,andthedocumentobject. SystemManageralsocontrolsapplicationdomains,whichareusedtopartitionclassesbythesecuritydomains(seethedescriptionoftheApplicationDomainclassintheFlexLanguageReference). RIAWITHADOBEFLEXANDJAVA
45
CHAPTER2
Ifyou’llbedevelopingcustomvisualcomponents(descendentsoftheUIComponentclass),keepin mindthatinitiallysuchcomponentsaren’tconnectedtoanydisplaylistandSystemManager=null. OnlyafterthefirstcalloftheaddChild()willaSystemManagerbeassigned.Youshouldn’taccess SystemManagerfromtheconstructorofyourcomponent,becauseitcanstillbenullatthemoment. Ingeneral,whentheApplicationobjectiscreated,thefollowingstepsarebeingperformed: 1. Instantiationoftheapplicationobjectbegins. 2. InitializationoftheApplication.systemManagerproperty. 3. Theapplicationdispatchesthepre-initializeeventatthebeginningoftheinitializationprocess. 4. ThecreateChildren()methodiscalledontheapplicationobject.Atthispointeachofthe application’scomponentsisbeingconstructed,andeachcomponent’screateChildren()will alsobecalled. 5. Theapplicationdispatchestheinitializeevent,whichindicatesthatalltheapplication’scomponentshavebeeninitialized. 6. AcreationCompleteeventisbeingdispatched. 7. Theapplicationobjectisaddedtothedisplaylist. 8. TheapplicationCompleteeventisbeingdispatched. Inmostcases,you’llbeusingtheMXMLtagtocreatetheapplicationobject,but ifyouneedtocreateitinActionScript,don’tcreatecomponentsinitsconstructor–usecreateChildren()forthis(forperformancereasons). FlexSWFfileshaveonlytwoframes.TheSystemManager,Preloader,DownloadProgressBar,and ahandfulofotherhelperclassesliveinthefirstframe.TherestoftheFlexFramework,yourapplicationcode,andembeddedassetslikefontsandimagesresideinthesecondframe.WhenFlash PlayerinitiallystartsdownloadingyourSWF,assoonasenoughbytescomeforthefirstframe,itinstantiatesaSystemManagerthatcreatesaPreloader,whichinturncreatesaDownloadProgressBar andthesetwoobjectsarewatchingtherestofthebytestreaming-inprocess. Whenallbytesforthefirstframearein,SystemManagersendstheenterFrametothesecondframe andthendispatchesotherevents.Eventually,theapplicationobjectdispatchestheapplicationCompleteevent. Youmaybywonderingwhyyouneedtoknowwhat’shappeningbeforetheapplicationloads.This knowledgemaycomeinhandyespeciallyifyourapplicationislargeandusersexperienceunpleasantdelaysduringtheinitialload.Youcanusethistimeproductively,forexample,tolettheuserlog ontothisapplication.Thiswillalsogivetheuseraperceptionthatyourapplicationloadsfaster. FarataSystemshascreatedanopensourcelogoncomponentthatcanbeusedinPreloader(see http://flexblog.faratasystems.com/?p=88). You can also create a splash screen that fades away with an image of your choice by substitut-
46
RIAWITHADOBEFLEXANDJAVA
GettingFamiliarwithFlex
ingthestandardFlexPreloaderandtheDownloadProgressBarobjectswiththecustomones.Ted PatrickfromAdobehasprovidedsampleapplicationsthatdisplayasplashscreenusinganimage fromPNG,SWF,orGIFfiles(seehttp://www.onflex.org/ted/2006/07/flex-2-preloaders-swf-pnggif-examples.php).Whileyoursplashscreenisdisplayed,youcandownloadsomeothersystem resourcesand/orlibrariesandthenthesplashscreenfadesaway. Here’sanotheridea.Duringtheloadingprocess,yourActionScriptcodemayinteractwiththe“outside world”akaWebbrowser.TheclassExternalInterfacehasamethodaddCallback()thatletsyouregister anActionScriptmethodascallablefromthecontainer.AfterasuccessfulinvocationofaddCallBack(), theregisteredfunctioninFlashPlayercanbecalledbytheJavaScriptorActiveXcodeinthecontainer. You’llfindmoreontheExternalInterfaceinChapter15. Onemoresuggestion:yourapplicationmayneedtoloadanotherapplication(s)fromanSWF.If youhavetodoitinActionScript,youcanusetheSWFLoaderclassfromtheapplicationComplete event(seemoreonSWFLoaderinChapter10).
Summary Inthischapter,wefirstcreatedsimpleapplicationsandpeekedunderFlex’shood.ThemostimportantfeatureofFlex2isthatit’sanopenandextendableframework.Ifyouareaccustomedto beingincompletecontrolwithJava,Flexdoesn’ttieyourhandseither.Inthenextchapterwe’llget familiarwiththeEclipse-basedIDEcalledFlexBuilder.
Endnotes 1. BesidesmxmlcandcompcFlexhasonemorecompilercalledtheWebtiercompiler.Thiscompilercomes withFlexDataServicesandletsyoucreateswffilesduringtheruntimeontheserverside.Thiscompiler runsinaJavaEEservletcontainer. 2. FlexcompileritselfisjustawrapperthatinternallycallstheJavaarchivecalledmxmlc.jar(orcompc. jar).ThiscompilationprocesscanbeinitiatedfromaJavaprogramifneeded.Seehttp://livedocs. macromedia.com/flex/2/docs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_ Parts&file=00001489.html. 3. Ifyou’reusingaWeb-tiercompiler,yourusercanspecifyaURLoftheremoteMXMLfile,whichwillbe compiledintotheswffileanddeliveredwithoutanHTMLwrapper. 4. YoucanskipthisstepifyouhaveEclipseinstalled–Antislocatedinitsplug-insdirectory.
RIAWITHADOBEFLEXANDJAVA
47
48
RIAWITHADOBEFLEXANDJAVA
CHAPTER
3
FlexBuilderDevelopmentEnvironment
RIAWITHADOBEFLEXANDJAVA
49
CHAPTER3
FlexBuilderDevelopmentEnvironment
InstallingandConfiguringFlexBuilder The Flex Builder IDE is based on Eclipse and it’s a more productive environment compared to programmingwithFlexinatexteditor.ThisIDEcomesintwoflavors:astandaloneversionand anEclipseplug-in.You’llhavetochooseoneoftheseversionsduringtheinstallationprocess.But thestandaloneversionofFlexBuilder2.0doesn’tcontainJavaDevelopmentplug-ins,alsoknown asJDT(JavaDevelopmentTools).YouneedJDTtosupportJavaprojects,perspectives,etc.,sowe recommendinstallingtheplug-inversionofFlexBuilderontopofthepre-installedEclipse3.2. DuringtheFlexBuilderinstallprocess,you’llhavetospecifythedirectorywhereyourEclipseresides(theonethathasasub-directorycalledplugins). AfterFlexBuilderisinstalled,startEclipse,selectthemenusWindow|OpenPerspective|Other, andopentheFlexDevelopmentperspective.TheFlexDebuggingperspectivewillbeautomatically addedtotheEclipseIDE.TheFlexBuildercomeswithgreathelpcontent,whichintheplug-inversionislocatedunderthemenuHelp|HelpContents|AdobeFlex2HelporHelp>FlexStartPage.
YetAnotherHelloWorld We’re not going to bore you with all the little details of the Eclipse-based Flex Builder, because they’recoveredprettywellintheproductmanualUsingFlexBuilder2.Youcanalsoreadthesection onFlexBuilderusingitshelpmenu.We’dratherwalkyouthroughtheprocessofcreatingasimple Flexprojectwithseveralbasicapplications.Whilebuildingtheseapplications,we’lltouchonvariousareasoftheFlexBuilder’sworkbenchtogiveyouaquickfeelforthefunctionality. Always start by creating a new project. Select the File>Flex Project menu item to open the New Projectwizard.Thefirstdecisionyou’llhavetomakeistochoosethewayyourapplication(infact, alltheapplicationsinthisproject)isgoingtoaccessremotedata:
50
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
Figure3.1CreatingtheFlexproject–projecttypeselection
We’lldiscussFlexBuilder’sprojecttypeslaterinthischapterinthesection“ProjectTypesandData Access.” Since we’re not going to use any server-side technology in this chapter, the Basic type isourchoicesojustpresstheNextbutton.Nowyou’llhavetopickthenameandlocationofthe project:
Figure3.2Namingtheproject
Onceyou’redone,clickNextand,inthefollowingscreen,specifythenameofthemainapplication fileandoutputfolder:
RIAWITHADOBEFLEXANDJAVA
51
CHAPTER3
Figure3.3Namingthemainapplicationfile
EnterHelloWorld.mxmlasthenameofthemainapplicationfile.Thecompiledapplicationwillbe savedinthebindirectoryunlessyouspecifyanotheroutputfolder.ClickFinishtocompletethe projectcreation. Aftertheprojectiscreated,you’llseeascreensimilartoFigure3.4withthepre-generatedfileHelloWorld.mxml.
52
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
Figure3.4Anewprojecthasbeencreated
TheapplicationmayalsoincludeotherMXMLandActionScriptfileswithapplicationlogicaswell asimagesandotherresourcescommonlyreferredtoas“assets.” Haveyounoticedthetwotabsabovethecodeeditor?Bydefault,FlexBuilderdisplaysthesource code view, but since we have to design our HelloWorld screen, we’ll start working in the design mode(asyoubecomemoreexperienced,you’llbeusingthedesignmodemostlyforquicklyprototypingyourscreen),soclickontheDesigntabandletthefunbegin.
Figure3.5Adesignmodetab
RIAWITHADOBEFLEXANDJAVA
53
CHAPTER3
InthemiddleofthescreeninFigure3.5you’llseehowyourfuturewindowwilllook.Allfolders withpredefinedFlexcomponentsarelocatedontheleft:layouts,navigators,standardandcustom controls.SelecttheLabelcontrol,thendrag-and-dropitontothescreen. InthemiddleofthescreenyoucanseecanvasoftheApplicationcontainer.AvailableFlexframeworkcomponents(aswellascustomonesthatyoudevelopforyourproject)arepresentedinthe treeviewintheComponentspanelinthelowerleft.SelecttheLabelcontrolparentedbyControls, thendrag-and-dropitontothescreen.Changethelabel’stextto“Hello,World!”Now,switchtothe CategoryViewintheFlexPropertiespanel,locatedinthelowerrightcornerofthescreen.Inthe StylescategorysetthefontSizeto22andfontWeighttobold. Ifyoudidn’tselectaspecificcontrolpriortochangingthefontattributes,itwouldaffectallthe controlsofthiswindow.
Figure3.6Dropthelabelcomponentonthedesignpanel
Nowtakeapeekatthesourcetab.FlexBuilderhasgeneratedalinesimilartothisone:
andtheentireapplicationnowlookslike:
54
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
Listing3.1HelloWorld.mxml
WorkingwiththeSourcePanel Typethespaceanywhereinsidethetagandenjoyinstantaccesstoalltheattributes includingfontSizeandfontWeight:
Figure3.7Tagattributescodehinting
Infact,youcouldhavejusttypedtheletter“f”andthelistofattributeswouldbeconvenientlylimitedtotheonesstartingwith“f.” Similarly,wereyoutotype“FlexApplicationandyoushouldseeascreenlikethis:
Figure3.9RunningHelloWorldinaWebbrowser
Congratulations!YourfirstFlexBuilderprogramisupandrunning. Right-clickinsidethiswindow.You’llseethepop-upmenuwithouttheViewPageSourceoption. Nowonder,yourapplicationhasbeencompiledandlaunchedinsidethevirtualmachinecalled FlashPlayer,that’swhyyoucan’tseeitssourcecode.Actually,attheendofthischapterwe’llshow youhowyoucanenablethisViewSourceoptionandmakeyourproject’ssourcecodeavailableto thegeneralpublic. Butyoucanalsoopenthebindirectory,double-clickontheFlashmovieSWFfile,andyourFlash playerwillrunyourprogramwithoutthebrowser.NowtheFlashPlayerwillrunyourapplication independentlyoftheWebbrowser:
56
Figure3.10RunningHelloWorldintheFlashplayer
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
BuildingtheProject Tobuildsimpleapplicationslikeours,thecodeiscompiledintoasinglecompressedarchivewith an.swfextension,whichissavedintheoutputdirectorybasedontheFlexBuildersettingsofyour project,whichisbinbydefault.YoucanchangethisusingthemenusProject|Preferences|Flex BuildPath.Duringtheprojectbuild,FlexBuilderalsocreatesahandyHTMLwrapperforyour.swf file,soyoucantestitinaWebbrowser. Your project can be built either automatically (every time you save code changes) or manually basedontheBuildAutomaticallysettinginthemenuProject.Typically,buildsaredoneincrementallybasedonthedatayou’vemodifiedsincethelastbuild.Butifyou’dliketorebuildtheentire projectfromscratch,justusethemenuitemProject|Clean. Ifyouspecifiedthatyourprojectdependsonotherones(seetheProjectReferencesscreeninthe Propertiesmenu),FlexBuilderwillfigureouttheorderoftheprojectbuilds,oryoucanspecifythe orderusingthemenusWindows|Preferences|General|Workspace|BuildOrder. Formoresophisticatedprojects,buildsmayconsistofmultiplestepsincludinginvocatingadditionalprograms,copyingfiles,etc.FlexBuildersupportsusingtheApacheAnttool,andwe’lluse AntinChapter10.
FlexBuilder-GeneratedFiles Whenyourapplication,say,HelloWorld.mxml,iscompiledforthefirsttime,you’llfindthefollowingfreshlygeneratedapplication-specificfilesinyouroutputfolder: HelloWorld.swf:Thebytecode(113Kb)readytorunintheFlashPlayer HelloWorld.html:AnHTMLwrapper(5Kb)torunintheWebbrowser HelloWorld-debug.swf:Thebytecodewithdebuginformation(180Kb)toruninthedebugversion of Flash Player (this version of the player is installed automatically with Flex Builder). HelloWorld-debug.html:AnHTMLwrapper(5Kb)torunthedebugversionofHelloWorld.swfin yourWebbrowser Intheoutputdirectoryyou’llalsofindsomefilesthataresharedbyalltheapplicationsfromyour project: AC_OETags.js:AbunchofJavaScriptfunctionsusedbytheHTMLwrapper,i.e.,theuser’sFlash Player’sversiondetection playerProductInstall.swf:Asmall(1Kb)usedbytheHTMLwrappertoupgradetheuser’sFlash Playerplugintoversion9,ifneeded history.*:TheFlex’simplementationofthehistoryfunctionality(7Kbtotal),similartothehistory inWebbrowsers TodeployyourapplicationunderaWebserver,you’llneedtocopytherealloftheabovefilesexcept thedebugonesandprovideyouruserwiththeURLofthefileHelloWorld.html. RIAWITHADOBEFLEXANDJAVA
57
CHAPTER3
Whilebuildingawrapperisanautomatedprocess,chancesareyou’llhavetotweakittosetcertain options in the browser/application integration like history management or setting the DHTML objectsforFlex/JavaScriptinteraction.Youwillneedtoreviewthefilesinthehtml-templateused togeneratetheHTMLwrapperandcustomizethemtofityourneeds. YourresultingSWFfilemayusesomeoftheexternalresources,forexample,imagefiles,whichmay eitherbeembeddedinyourSWFfileswithan[EMBED]tag,orremainexternal.Inthelattercase youcanreplacetheimagefileswithoutneedingtorecompileyourSWF.JustmakesurethattheoptionCopynon-embeddedfilestooutputdirectoryisselectedintheprojectpropertiesscreenbelow. YoucanalsoturnoffthegenerationoftheHTMLwrapper.
Figure3.11Configuringcompilerproperties
RunningApplicationsinFlexBuilder AtypicalprojectconsistsofmultipleMXMLandActionScriptfilesandonlyoneofthemshouldbe createdasanapplicationbyincludingthetagasopposedtoMXMLcomponents thatdon’tneedit.Onasimilarnote,Javaprojectsconsistofmultipleclasses,butonlyoneofthem shouldhavethemethodmain()thatmakestheclassexecutable.Duringthedevelopmentstage, though,youmightwanttohavemorethanoneMXMLfilewiththetag,which raisesthequestion:whichoneofthesefileswillbeconsideredthemainonewhenyourunyour project?Tosetthedefaultapplicationfileoftheproject,right-clickonthefileandselecttheoption SetastheDefaultApplicationfromapop-upmenu. Youcanalsorunnon-defaultMXMLfilesfromyourprojectaslongastheyhavetheApplication tag.Justright-clickonthefileandselectthemenuoptionRunasaFlexApplication.Youcanalways
58
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
check which MXML files can run by selecting the Project Properties menu and the option Flex Applications.You’llseeapop-upscreenthatwilllistallcurrentrunnablefilesandwillletyouadd (remove)moreMXMLfilestothislist.
SimpleEventProcessing ThenextgoalistostartgettingfamiliarwithFlexeventprocessingandunderstandtherelations betweenMXMLandActionScriptlanguages. We’regoingtocreateanewapplicationcalledHelloBasilthatwillcontainthreecomponents:atext field,abutton,andalabel.Theuserwillenteranameinthetextfield(i.e.,Basil),pressthebutton, andthelabelcomponentwilldisplaytoday’sdateandagreeting(i.e.,“May1,2006:Hello,Basil”). Thisapplicationwillhavetoprocessthebutton-clickeventswiththehelpofActionScript.
APureMXMLVersion IntheFlexBuilder’smenuselectFile|New|FlexApplicationandenterHelloBasilastheapplication name.Usingthedropdownatthebottomofthedialog,selecttheverticallayoutandpressFinish. DragandnowdroptheTextInputcomponentfromtheComponentsviewontheleftontothedesignarea.Enterinput_1astheIDofthiscomponent. Next,addaButtoncomponentandsetitsIDtobtn_1anditslabelto“GreetMe”,thenputtheText componentunderthebutton.RemovethedefaulttextandsetitsIDtooutput_1. SwitchtotheSourcePanetoseethegeneratedmxml:
Listing3.2HelloBasil.mxml Runtheprogram.Ifyoupressthe“Greetme”button,itwon’tdomuchsincethere’snocodeyetfor processingthebuttonclickevent:
RIAWITHADOBEFLEXANDJAVA
59
CHAPTER3
Figure3.12RunningHelloBasil
SpecifyingEventsHandlers Let’saddtheprocessingofabuttonclickevent.Typeaspacebeforetheforwardslashinthelinetoactivatethelistoftagattributes,andtype“c”toacceleratenavigationtoattributes thatstartwith“c”:
Figure3.13AsampleofFlexBuildercodehinting
SelecttheclickattributeandpressEnter.Alternatively,youcouldhavejusttypedclick=””.We’re goingtofillintheblankvalueoftheclickattributewiththescript,correspondingtotheaction thisbuttonwillperform:wewanttosetthetextpropertyofoutput_1_totheconcatenated“Hello” andthetextofinput_1.FlexBuilderwillkeephelpingyoubysuggestingavailablepropertiesofthe screencomponentsasyoutype.Thenewversionofourbutton’scodewilllooklikethis:
Runtheprogram,typeinthename,clickthebutton,andifyournameisBasil,yourscreenwilllook likethis:
60
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
Figure3.14AfterclickingtheGreetMebutton
As alternative to coding the script directly inside the click attribute, we could have moved this scripttoanapplication-levelfunctionandcalledit:
>
Listing3.3HelloBasil.mxmlwithActionScript FlexMXMLsupportsembeddingActionScriptastheliteralvalueofthetag.Generally speaking,it’ssafertosafeguardyourcodeintheCDATAsectionsinceitmaycontainsymbolssuch as“
>
Listing3.5Addingassertionsandtracingfordebugging Totoggleabreakpointjustdouble-clickonthegrayareatotheleftofthelinewhereyouwanta programtostop,andFlexBuilderwillmarkthislinewithabluebullet.We’llputabreakpointatthe lastlineofthegreetMe()method.Toruntheprograminthedebugmode,selecttheapplicationin theNavigatorviewandselect“DebugAs…”inthecontextmenu. Assoonasyoupressthe“Greetme”button,yourprogramwillreachthebreakpointandFlexBuilderwillpausetogiveyouachancetoanalyzethevaluesinthevariablesyou’reinterestedin.Tostep throughtheexecutionofyourprogram,youcanusethelittleyellowarrowsatthetopofthedebug view.
RIAWITHADOBEFLEXANDJAVA
67
CHAPTER3
Figure3.19DebuggingwithFlexBuilder
TheVariablesviewletsyoucheckandchangethevaluesofyourprogramvariables.Ifavariable represents a composite object, you can easily see its content by pressing the little plus sign on the right. For example, the variable this represents the object you’re in – your main application instance.IntheConsolewindowwecanseetheresultsofourtracestatements.Pleasenotethe assert()method–thistechniqueisoftenusedtoreducetheamountoftraceoutputiftheentries complywiththelistedassertions. FlexBuilderallowsyoutoperformconditionaldebugginginsteadofsettingupthebreakpoints. JustcallthemethodenterDebugger(): if (myVar > 25) enterDebugger();
Youstillneedtorunyourapplicationinthedebugmode,butifmyVarisnotgreaterthan25,Flex Builderwillexecuteyourprogramwithoutstoppinginthedebugger.
ProjectTypesandDataAccess OneofthemajordifferencesbetweenFlex2andpreviousversionsisitsabilitytocompileyour
68
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
code on a standalone client machine without using any servers. All the samples in this chapter wherecompiledlocallyontheclientmachineandtheydidn’trelyonanydatalocatedonexternal servers,soourprojectisbasic.However,yourapplicationcandirectlytalktoaremoteWebService despitethefactthatyourprojectisbasic. Ontheotherhand,inChapters5and6we’llbeusingFlexremoteobjectsanddataservices,and we’llselectanoptionalFlexDataServicesthatassumesyoualreadyhaveitinstalledanddeployed undertheJavaEEservletcontainerofyourchoice.Forthesetypesofprojects,you’llbeaskedto specifytheURLofyourFlexDataServicesinstall(we’lldothisinChapter5),andaftercompiling, yourprojectwillbeembeddedintoyourSWFfile. IfyouselectFlexDataServicesasyourprojecttype,you’llhavetospecifyifyou’replanningtocompileyourprojectlocallyusingFlexBuilderorontheserverwhenthepageisviewed.
GeneratedActionScript Aswe’vementionedbefore,theFlexcompilerconvertsMXMLintoActionScriptsourcecodeand thencompilesitintoanswforswcfile.Ifyou’dliketoseethisActionScriptcode,addanadditional compilerargument–keep-generated-actionscriptinthescreenshownbelowandrecompileyour project.FlexBuilderwillcreateasub-directorycalledgenerated,whereyoucanfindthesource codeofallthegeneratedActionScriptfiles.
Figure3.20Askingthecompilertokeepgenerated ActionScriptfiles
RIAWITHADOBEFLEXANDJAVA
69
CHAPTER3
After closing the dialog, you’ll also see the new“generated” folder in Flex Builder’s project tree containingabout50ActionScriptfiles.Thosefilesareusedtoinitializeaframework,provideCSS support,anddivideourapplicationintoresources,classesthathavetobeinitializedontheapplicationsload,etc.Wehighlyrecommendthatyouspendsometimereviewingthegeneratedcode afterlearninganewfeature/controltogetfamiliarwiththedependenciesandintercommunicationswiththeFlexframework.
Figure3.21Anauto-generatedActionScriptforHelloWorld.mxml
FlexBuilderTipsandKeyboardShortcuts FlexBuilderhasmanyconvenientkeyboardcommandsthatmakeyourprogrammingfaster.For thecompletelistofallavailableshortcuts,pressCtrl-Shift-L.We’velistedsomeoftheFlexBuilder tipsandthekeyboardshortcutsherethatweuseonaregularbasis: •
70
Ifyouseealittleasteriskinthetabwiththefilename,thismeansthatthisfilehassomeunsavedcodechanges. RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
•
HighlightthenameoftheclassorafunctionandpressthebuttonF3onyourkeyboard.This willtakeyoutothesourcecodeofthisclass.
•
Ifsomeofthelinesaremarkedwithrederrorcircles,movethemouseoverthecircleandFlex Builderwilldisplaythetextdescribingthiserror.
•
PressCtrl-Spacetogetcontext-sensitivehelp.
•
Alt+/willcompletethenameofthevariableoraclassafteryoutypedinacoupleofitsfirst letters.
•
PressCtrl-F11torunthelastlaunchedprogramagain.HittheF11keyalonetoruntheprogramindebugmode.
•
PlacethecursorafteracurlybraceandEclipsewillmarkthematchingbrace.
•
HighlightalineorablockofcodeandpressAlt-ArrowUporAlt-ArrowDown.Theselected codewillmoveupordown.
•
Ctrl-Hwillopenapopuptosearchonmultiplefiles,andCtrl-Fwillletyoufindtextinthe openfile.
•
HighlighttheMXMLtagorActionScriptclassandpressShift-F2.Thiswillopenthescreen withthelanguagereferencehelpontheselectedelement.
•
HighlightablockofcodeandpressCtrl-Shift-Ctocommentouttheselectedblockofcode.
•
Toselectsurroundingcontainers,selectaGUIcontrolandpressF4.
•
KeeptheControlkeydownwhilemovingthemouseoverMXMLtags.FlexBuilderwillshowa tooltipwiththenameofthecorrespondingActionScriptclass.Ifthetagturnsintoahyperlink andthesourcecodeisavailable,clickonthelinktoopenthesourcecodeoftheclass.
•
Asyouchangeyourcodeintheeditor,FlexBuilderputsaverticalpurplebarontheleftof eachmodifiedline.Thisisquitehandyifyou’dliketohaveaquicklookatthenewcodebefore pressingthebuttonSave.
•
Ifyouwanttotestyourapplicationindifferentbrowsersinstalledonyourcomputer,select themenuWindows|Preferences|General|WebBrowserandselectthebrowsertobeusedby FlexBuilderforlaunchingyourapplication.
•
Ifyouseeanerrormessagethatreads“…ChecktheErrorLogfile”,thislogfileislocatedin yourworkspace,indirectorycalled.metadata. RIAWITHADOBEFLEXANDJAVA
71
CHAPTER3
PublishingtheApplicationSourceCode Ifyou’dliketomakethesourcecodeavailabletotheusersofyourdeployedapplications(similar totheViewSourceoptionintheWebbrowsersinthinclients),usethemenuProject|PublishApplicationSource. Thismenuitemwillquicklybuildasetoffilesinthesub-foldersrcviewintheoutputfolderwith thesourcecodeofyourprojectreadytobepublishedontheWebasinFigure3.23.
Figure3.22Selectingthesourcecodetobepublished
Besidespreparingthesourcefilesforpublication,FlexBuilderwillalsoaddtotheApplicationthe attributeviewSourceURLpointingatthelocationofthesourcecode:
Listing3.6EnablingtheViewSourceoption
72
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
Figure3.23Publishedsourcecodeinthefolderbin/srcview
Nexttimeyourunthisapplication,right-clickingthemousewillopenupamenuwithoneextra item:ViewSource.SomethingyougotaccustomedtointheHTML/JSP-basedapplication,butalot betterorganized.
Figure3.24Therun-timemenu displayedonright-click
UsingtheSubversionVersionControlSystem EclipseandhenceFlexBuildercomewithapre-installedCVSplug-inthatyoucanuseasyourversioncontrolsystemwithoutanyadditionalclientinstallations.OpenthemenusWindow|Open Perspective|Otherandyou’llseetheCVSRepositoryExploring(seeFigure3.25). ButCVSisn’tyouronlychoice.Subversionisanotherpopularopensourceversioncontrolsystem that’seasiertoadminister,allowsversioningsupportfordirectories,andhassomeotheradvantages.Subversionisavailableforfreeathttp://subversion.tigris.org/.Youcanalsodownloadand RIAWITHADOBEFLEXANDJAVA
73
CHAPTER3
install one of the plug-ins for Eclipse (i.e., Subversive or Subclipse), which puts your repository justoneclickawayfromyourFlexBuilderprojects.AfreeonlineversionoftheSubversionbookis availableathttp://svnbook.red-bean.com/andherewe’lljustprovidetheinformationrelevantfor configuringtheFlexBuilderclienttobeusedwithSubversionrepositories. WeassumethatyoualreadyhavetheSubversionserverinstalled,thesourcecoderepositoryfor yourprojectconfigured,andtheproperpermissionstoaccessthisrepository. StartbydownloadingandinstallingtheSubversionclient,whichwillletyoutodoalltheversion controloperationsfromthecommandline.WhilemostofthetimeyoumaybeusingsomeSubversionplug-infromFlexBuilder,thisclientsoftwarewillbecomehandyifyoudecidetoautomate yourapplication’sbuildprocess.Forexample,youmaywriteanAntbuildscriptthatwillstartby runningSubversioncommandscleanly,checkingoutallthefilesfromtherepository. YoucandownloadandinstallprepackagedbinariesofSubversionathttp://subversion.tigris.org/ project_packages.html. AfterinstallingyourclientmakesurethatthesystemvariableAPR_ICONV_PATHisdefinedand pointsattheiconvdirectoryinyourSubversionclientdirectory.IfyourSubversionserverislocated ontheUnixboxaccessedusingssh,alsodefineavariableSVN_SSHthatpointstothelocationof yoursshclient.Forexample: SVN_SSH=C:\\my_ssh_client\\ssh2.exe
ThenextstepistoinstalltheSubversionplug-in(weuseSubclipsehere)soyoucanusetheSubversionrepositoryrightfromyourFlexBuilderprojects.SelectthemenuHelp|SoftwareUpdates |FindandInstall,andinthepop-upscreenpicktheoptionSearchfornewfeaturestoinstall.In thenextscreenenterSubclipseinthenamefieldandthefollowingURLinhttp://subclipse.tigris. org/update_1.0.x.
74
Figure3.25Eclipseperspectives includingtheSubversionplug-in
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
ChecktheSubclipseboxinthenextscreenandfinishtheinstall. Afterasuccessfulinstall,you’llfindanewperspectivecalledSVNRepositoryExploringasinFigure 3.25.NowtheEclipseHelpmenuwillhaveanewsectiononusingSubclipse. Finally,youneedtospecifythelocationoftheSubversionrepositoryofyourproject:thesystem administratorwhoinstalledandconfiguredtherepositorywillgiveyoutheparametersoftheSubversionserver.Aftergettingthisinformation,right-clickintheNavigatorview,selectNew|SVN Repository,andenteritsURL.TheEclipsehelpmenuhasasectiononit. Ifyou’reconnectedtoyourrepository,right-clickonyourprojectandselecttheTeamitemfrom the menu. It should open a pop-up windowwiththetypicalversioncontroloptions:Checkout, Update,Commit,Diff,etc. Ifyou‘dliketoaddanexistingprojecttotherepository,usethemenuTeam|ShareProject.Toget the project from repository into Flex Builder, open your SVN Repository Exploring perspective, right-clickonaproject,andselecttheoptionCheckOutasProject.Therestofthecommandsare prettyintuitive(don’tforgetaboutcommittingyourchanges)andarewelldescribedintheSubversiondocumentation. Ifforanyreasonyou’renothappywiththeSubclipseplug-in,trytheSubversiveclient,whichis availableathttp://www.polarion.org/.
Figure3.26SelectPerspectivewithSubclipseplug-in
RIAWITHADOBEFLEXANDJAVA
75
CHAPTER3
Summary In this chapter we’ve introduced you to a developer-friendly Flex Builder tool. Even though our sampleapplicationswereverybasic,you’vehadachancetoseeandenjoytheeaseofdevelopingin FlexBuilder.YoucanfindadetaileddescriptionofallFlexBuilderfeaturesinthemanualusingFlex Builder2,whichisavailablefordownloadatwww.flex.org.Inthefollowingchapterswe’llgradually increasethecomplexityoftheexamples,sofastenyourseatbelts,please.
76
RIAWITHADOBEFLEXANDJAVA
FlexBuilderDevelopmentEnvironment
RIAWITHADOBEFLEXANDJAVA
77
78
RIAWITHADOBEFLEXANDJAVA
CHAPTER
4
LearningFlexThroughApplications
RIAWITHADOBEFLEXANDJAVA
79
CHAPTER4
LearningFlexThroughApplications
Inthischapterwe’llrollupoursleevesandbuildseveralmini-applicationstoillustratetheuseof variouselementsandtechniquesofbothActionScript3.0(AS3)andMXMLthatyou’llbeusingin real-worldprojects.Butthisisnotatutorialandwe’llbeintroducingtheseelementsonanas-neededbasis.Ourgoalistogiveyouthebigpicture,butyoushouldstudytheproductdocumentation fordetailedcoverageofeachoftheseelements. Inparticular,thischaptercontains: • • • • • • • • •
AdescriptionofsomeoftheAS3featuresthatmayseemunusualtoJavaprogrammers Areviewofobject-orientedprogrammingtechniquesinAS3 AshortdescriptionofECMAScriptforXML,whichofferssimplifiedprocessingoftheXML sources AnoverviewoftheFlexeventmodel AnapplicationthatusesnamespacesinAS3 AprogramthatusesFlexdatabindingandregularexpressions AnapplicationillustratingthecommunicationbetweenFlexandJavaServerPages Asampleapplicationshowingdifferentwaysofachievingthesameresultsbypeoplewithdifferentprogrammingbackgrounds Anapplicationillustratingtheuseofcollections,filters,andtimers
ActionScriptDynamicClasses LikeActionScript2andJavaScript,AS3letsyouaddandremovepropertieson-the-fly.Inparticular, it’spossibletoaddpropertiesprogrammaticallyandaddorredefinemethodsduringruntime. Let’sconsiderthefollowingPersonclass: dynamic class Person { public var firstName:String=”Joe”; public var lastName:String=”Doe”; }
80
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
WecancreateaninstanceofPerson,addthepropertyage,andoverridethedefaultmethodtoString(): var p:Person = new Person(); p.age=25; // dynamic property p.toString = function():String { // overridden method return p.lastName + “,” + p.firstName + “-” + age; } trace(p); // Doe,Joe-25
Notethatdynamicallyaddedorredefinedfunctionscan’taccessprivatemembersofthedynamic class:hadwedeclaredPerson’slastNameasprivate,trace(p)wouldresultin“undefined,Joe-25.” Addingorredefiningamethodisn’tanydifferentfromthatofanydifferentpropertytype.Afterall, afunctionisjustapropertywithatypeFunction. SupposetheJavaProgrammerclassisdefinedas:
}
public class JavaProgrammer { public var lastName:String=”Mr. Interface”; public function learnFlex():void { trace(lastName + “ is ready for RIA work!”); }
ThenaninstanceofPersoncan“attach”themethodlearnFlex(): var p:Person=new Person(); var jp:JavaProgrammer = new JavaProgrammer(); p.boostCareer = jp.learnFlex;
andtheninvokethemethod: p.boostCareer();
// Mr. Interface is ready for RIA work!
TheattachedfunctionretainsthescopeoftheoriginalinstanceofJavaProgrammer:itprints“Mr. InterfaceisreadyforRIAwork!”–not“DoeisreadyforRIAwork.” Mostimportantly,however,isthatthedefinitionofPersonstartswiththekeyworddynamic.What makesAS3,asascriptinglanguage,standoutfromitspeersistheoptionalstrongtypingand,first ofall,thejust-in-time(JIT)compilationofstronglytypedcodeblocksintonativemachinecode. Havingbetonperformance,AS3changedtherulesofengagement:nowinsteadofassumingthat RIAWITHADOBEFLEXANDJAVA
81
CHAPTER4
theclassallowsdynamicpropertiesormethods,itdefaultstotheopposite.Regularclasses,asthey areknowninJava,arecalledsealedinAS3.Totakeadvantageofdynamicpropertiesandmethods youhavetoprefixtheclassdefinitionwiththemagicworddynamic. Whathappensifyoudon’t?Forstarters,thedefaultmodeoftheFlexBuildercompilerisstrictand unlessyouturnitintoaso-calledstandard(strict=”false”),youwon’tbeabletocompiletheabove snippet.Then,havingturnedoffthecompile-timetypecheckingofAS3,you’dgethitbyruntime typechecking. InfullcompliancewithAS2andJavaScript,it’salsopossibletoadd/modifythepropertyormethod ontheentiredynamicclassviaitsprototypeobject: Person.prototype.age = 46; Person.prototype.fullName = function():String { return this.lastName + “,” + this.firstName ; } trace(p.fullName()); // Doe,Joe46
Noticehowthevalueofagehasbeeneffectivelymodifiedontheinstancepthat’salreadybeen created. Thedeleteoperatordestroysthepropertyofthedynamicobjectandmakesiteligibleforgarbage collection: delete p.age; trace(p); // Doe,Joe-undefined
SomeoftheFlexclassesareintentionallydefinedasdynamic,thosebeingObject,Array,MovieClip, NetConnection,andAsyncToken.Whenthischapterwaswritten,thesubclassesofthedynamic classeswerenotdynamicbydefault.Becauseofthis,youmayrunintoaruntimeerror:imaginea sealedclassSthatextendsadynamicclassD.Ifyoucreateanobjectas var dynObj:D =
new S(),
anattempttoaddapropertytodynObjwillproducearuntimeerrorbecausethevariabledynObj pointstoasealedobject.So, dynObj.favoriteBand=”Beatles”;
wouldcause ReferenceError: Error #1056 Can’t create a property. favoriteBand…
82
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Youshouldbeawareofthetradeoff:nativelycompiledcodeperformsfasterintheorderofmagnitude; it’salsosignificantlymoreefficientintermsofmemoryconsumption.Ontheotherhand,dynamic classesbringlotsofflexibilitytoprogramming.Forinstance,theyhelpyougetawayfrommarshaling incompatibilitiesduringremotecalls.ComparethiswithJavaRMI.Forthesuccessfulserializationof aclassbetweentwoJVMs,thedefinitionoftheclasshastobepresentoneachend1.Importantlyit hastobeexactlythesamedefinition.Butwithdynamicclassesit’snotamust.And,asfarasobjectorientedtermsgo,youcouldsaythatdynamicclassesoffer“O-Oonsteroids”(useatyourownrisk).
Methods,Functions,andClosures Inadditiontothemethodsofaparticularclass,ActionScriptsupportsuser-definedfunctions,such asapplication-levelfunctionsandpackage-levelfunctions.It’sabitunusualforJavaprogrammers thatnotalluser-definedfunctionsshouldbepartsofsomeclassdefinition,but,hey,inscripting languagesafunctionisanobjectofitself! You can define a function using either function statements or function expressions. A function statementdeclarationofthefunctioncalcTax()thatreturnsanumbermaylooklike: public function calcTax(income:Number):Number{ return income*0.28; } var myTax:Number=calcTax(70000);
Alternativelyyoumayuseafunctionexpression.Onetypicalusecaseforafunctionexpression, akaananonymousfunction,isassigningittosomepropertyofanobject.That’sexactlywhatwe’ve doneinthepriorsectionwhenwedefinedthefullNamemethod: Person.prototype.fullName = function():String { return this.lastName + “,” + this.firstName ; }
Here’sanotherexample:
var calcFunc:Function = function (income:Number):Number{ return income*0.28; } var myTax:Number=calcFunc(88000);
There’snofundamentaldifferencebetweenthelatterusecasesandaregularfunctionstatement, becauseafunctiondeclarationisequivalenttodefiningavariableofthetypeFunctionandassigningitafunctionexpression.There’sanuisance,however,whenananonymousfunctionisattached toapropertyofandynamicobject:whenthepropertygetsdeleted(fullName),thelifespanofthe functionisover2. RIAWITHADOBEFLEXANDJAVA
83
CHAPTER4
Anotherpopularusecaseforanonymousfunctionsistheeventhandlerfunction: remoteObject.addEventListener( “fault”, function(event:FaultEvent):void { // .. .. .. } )
NotethatthesyntaxabovecancausememoryleakssinceitrequirestheVMtomakeareferenceto thisobjectandplaceitinsideaglobalanonymousfunctionthatmakesgarbagecollectionunavailableon“this.”Properapplicationtechniquessaytouseaboundmethodandaweakreference/removeEventListenermethodtosimplifymemoryde-allocation.
FunctionParameters InAS3,primitiveparametersarepassedbyvalues,andnon-primitivesarepassedbyreference.You canincludedefaultvaluesforthefunctionparameters.Forexample: public function calcTax(income:Number, dependents:int=1):Number{ return (income*0.28 – dependents*500); //Give 500 credit for each dependent } var tax=calcTax(10000);
// tax is equal to 2300
WithinthefunctionbodyAS3supportsspecialobjectscalledarguments.Thesearearraysthatincludeactualargumentspassedtothefunction.Thelengthofthesearraysmaybedifferentfrom thenumberargumentsthefunctiondeclares.Theadditionalpropertyarguments.calleecontainsa referencetothefunctionitself. Also,theAS3-specificwayofsupportingavariablenumberoffunctionargumentsistheso-called …(rest)parameter.Ellipsesfollowedbythenamerepresentanarraythataccommodatesanynumberofargumentsthatyoupassinexcessofthedeclaredparameters,whichalwaysprecedetherest parameter: public static function calcTax(… taxParams):Number{ for (uint i=0; i< taxParams.length; i++){ trace(taxParams[i]); } }
Javaprogrammersmayfindthe…(rest)similartoavarargsnotation.Youcanmixthe…(rest)with otherfunctionparameterssolongasit’sthelastparameterofthefunction.
84
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Here’sanideaforusingthe…(rest)parametertoovercometheabsenceofoverloadedfunctions andconstructorsinAS3:useaswitchcaseoperatortobranchonthenumberofactualarguments: Class MyClass { public function MyCLass(...args) { switch (args.length) { case 0: constructor1(); return; case 1: constructor2(args[0]); return; case 2: constructor3(args[0], args[1]); return; ... }}
Toensureproperprocessingincaseswherethenumberofargumentsisthesamebutthetypesare different,you’dhavetocomplementthiswithtypechecks: if(args[0] is String) { // process as String } else if (args[0] is Number){ // process as Number }
GettersandSetters AS3 getters and setters form an alternative mechanism for defining object properties. UnlikeJava,wheregettersandsettersarepartoftheJavaBeansspecification,AS3getters andsettersarefirst-classlanguagecitizens.Accordingly,ratherthanrelyingontheconventionofset/getprefixesforfunctionnames,AS3offersset-and-getmodifierstofollow the function keyword. Here is an example of the classTax with the read/write property income: public class Tax extends EventDispatcher { private var myIncome:Number;
public function set income(inc:Number):void{ myIncome=inc; // perform related actions, dispatchEvents, etc. }
}
public function get income():Number{ return myIncome; }
Listing4.1ClassTax
RIAWITHADOBEFLEXANDJAVA
85
CHAPTER4
ThecodesnippetbelowinstantiatestheclassTaxandassignsandprintstheincomeusingthegetterandsetteroftheclassTax: var tx:Tax=new Tax(); tx.income=5000.00; trace(“The income is “ + tx.income);
FunctionsasObjects Javaprogrammersmayfindthisconceptabitunusual,butAS3functionsareobjectsofthetype Function,andtheycanhavetheirownpropertiesandinternalmethodsandcanbepassedtoother functionsasarguments.AFunctiontypecan’tbeextendedsinceit’sdefinedas“final”soyoucan’t manipulateitinanOOway.Functionsareusedineitheraboundorclosuremode.Boundfunctions areidenticaltoJavaonesinthattheyaredefinedwithintheclassandtheirlexicalenvironmentis theinstanceoftheenclosingclass. public class MyClass extends Label { public function addNumbers(a:Number, b:Number) : void { text = a + b; }…. }
TousetheaddNumbermethodyouhavetoprovideareferencetoaninstanceoftheMyClassobject.It’sadirectcalltotheinstanceandthecontextoftheinstanceisavailabletothefunction. YoucanfindotherexamplesofusingafunctionobjectinsomeoftheActionScriptmethods.For example,whenyouaddalistenertoprocessanevent,youregisterinObjectProxy.addEventListener(“myEvent,”myEventHandler)andcallanameoftheeventtoprocessandafunctionthatwill handlethisevent.NotethatthenameofthemyEventHandlerfunctionisnotfollowedbyparenthesesherebecausewe’renotcallingthefunctionmyEventHandler,butarepassingareferencetoits code,whichwillbecalledifandonlyifthemyEventeventintheObjectProxyobjectoccurs. Thefactthatyoucanpassjustthenameofthefunctionasaparametermakesitimpossibleforthe compilertocheckthatthispassedfunctioniscalledwithproperparameters,whichmaypotentiallyleadtoruntimeerrors.ThoroughtestingofFlexapplicationsisevenmoreimportantthanin Java.
Closures Closures play a cornerstone role in dynamic languages.They’re essential for implementing featureslikeOOorbuildingframeworks.Atthesametime,theformaldefinitionofclosuresdoesn’t reallyhelpinunderstandingthem.Let’sgothroughafewexamples.First,we’llshowwhatclosures looklikeandthenwe’llgiveyoutheirusepatterns.
86
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Itallstartswiththeuseofananonymousfunctionthathasaccesstovariablesintheouterlexical scope:
import mx.controls.Alert; private var greeting:String=”Hello”; private function doInit(evt:Event) : void { btn.addEventListener(“click”, function(evt:Event):void { Alert.show( greeting + “, “ + txt.text); }); }
Listing4.2Anonymousfunctionaccessingouterscopevariables Compileandrunthiscode–itshowsamessageboxwiththevalueoftxt.textthatwasnotdefined intheclosure. Here’sanoversimplifiedthree-partdescriptionofclosures: 1 Closuresarefunctionsthataredefinedinaclassorfunctioncontextandpassedtoanother objectforexecutionatalatertime. 2. Aclosure’s“attachment”happensatruntime(andcanbeexecutedmultipletimesduringan applicationrun).It’sjustthestack-frameallocationwhereallcontextvariables(“greeting,”in ourcase)arebeingsavedforlateruse.Inmostcasesit’saboutsurroundingvariablesofthe hostingfunctionandcurrentruntimeclassinstance. 3. Finally,closure’sexecutioncanhappenatanytime,andcanalsohaveparameterswhenit’s called.AstandardconventionistohaveanEventobjectpassedinwithinformationfromthe callingobject. Itseemsyoucanuseclosuresto“snapshot”anynumberofparameters.Unfortunately,that’strue forsomedynamiclanguages,butnotforECMAScriptoneslikeActionScriptandJavaScript.Let’s illustratethedifferencewithafewexamples.First,let’smakesurethattheActionScriptclosuresare compile-timeartifactsratherthantruedynamicallyinterpretedcounterparts.Justswaptheorder oftheclosureandgreetingsdefinitionstatements.
RIAWITHADOBEFLEXANDJAVA
87
CHAPTER4
import mx.controls.Alert; private var myClosure :Function = function(evt:Event) { Alert.show( greeting + “, “ + txt.text); } private function doInit(evt:Event) : void { btn.addEventListener(“click”, myClosure); } private var greeting:String=”Hello”;
Listing4.3Closuresarecompile-timeartifacts Itstillworkseventhough“greeting”shouldhavebeenundefinedatthetimeoftheclosuredefinition–provingthatjustthereferenceisbeingused.Also,unlikeJava,thescopeofanobjectisthe stack-framecontentoftheenclosingfunctionorclass.Hereisanexamplethatwouldn’tcompilein Java,butisperfectlylegalinAS3: private function aaa():void{ { var a = 1; } //in Java a is not visible outside of the block Alert.show(a); }
Flash is a stack machine. A closure is a stack variable in the enclosing function, and this stackframeapproachgreatlysimplifiestheimplementationofclosuresandcodeoptimizersbasedon thestack,eventhoughitrequiressomeadjustmentsincodingstyle.Anotherissueisthatwedon’t haveobjectvalueshere–everythingisdonebyreference.Let’smodifythegreeting’svalueright beforethecall:
Asyoucansee,thegreetingwasreplacedonanalertscreenwiththenewvalue–itwouldn’thappen ifthe“closure”usedagreetingreferencebyvalueatthetimeofdefinition. Closuresarefirst-classcitizensofActionScript.Everymethodinyourclassisaclosure.That’show itknowstheinstancevariablesoftheclass.Essentiallyeveryclassisabigclosure.Youcanwritea functionwithclosuresinsidethatwouldbeverymuchaclassforallpracticalpurposes. Closuresareunavoidablewhenyouuseasynchronousoperationsorneedtoprocessaneventon
88
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
theotherobject.Almostanynon-trivialactioninFlex–communicationwiththeserver,say,orgettinginputfromauser–isasynchronous.Usingclosureautomaticallygivesyouthereferencetothe classinstanceinwhichyouhaveyourfunctiontotheexternalobjectprocessingtheevent.That’s sufficientforprocessingtheasynchronousmethod’sresultsinmostcases.Automaticpointingof thiscontexttotheinstancedefiningthefunctiongreatlysimplifiesthecodingsinceit’snaturalto thedeveloper. BeforeFlex2,inFlex1.5developerswereresponsibleforsupplyingcontexttotheclosure.Theabilitytoreplacetheclosurecontextgivesgreaterflexibilitytothecodeandmakesittrulydynamic. Thenextcodesampleshowsaclosureonanarbitraryobjecttoprovideacustomcontextobject: public class Closure extends Object { public static function create(context:Object, func:Function, ... pms):Function { var f:Function = function():* { var target:* = arguments.callee.target; var func:* = arguments.callee.func; var params:* = arguments.callee.params; var len:Number = arguments.length; var args:Array = new Array(len); for(var i:Number=0; i
Listing4.5Testingaclosure Nowthealertshows“Goodevening,world”becausethemethodhasbeenappliedusingadifferentcontext.Oftenthismethodologyiscalled“delegation”andisusedbybusinessframeworksto centralizetheprocessingofcertainevents. Theexampleaboveillustratestherelationshipbetweencontext,functions,andclosures.Usingthis techniqueletsyouimplementdynamicinheritance,polymorphism,andotherOOconcepts. Java6doesn’tsupportclosures,buttheymaybeintroducedinJava7.Closurefunctionsweren’t includedintheoriginalJavaspecificationbecausebackthentheplanwastoallowobjectcreation onlyintheheapmemoryifanewoperatorwascalled.ButJava5introducedauto-boxing,thefirst violationofthisprinciplebecauseobjectswerecreateddynamicallybehindthescenes.Ifyoufind theconceptofclosuresappealing,we’drecommendtakingalookatproductslikeBeanShellfor JavaandotherJavainterpretersthatletyoudefinewholeclassesorjustmethodsasclosures. TheclosestconstructstoclosuresinJavaareanonymousinnerclasses(seesection15.9.5ofthe JavaLanguageSpecification). Let’sconsideranexampleofaneventlistenerinJavaSwingwhereyoudefineananonymousclass atthemomentalistenerisadded.ThefollowingJavacodesnippetaddsaneventlistenertoabutton: JButton myButton = new JButton(“Place Order”); myButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae){ //your code to place an order goes here...
90
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications } } // end of anonymous class declaration );
ThiscodemeansthattheaddActionListener()methodinJavarequiresaninstanceofthelistener classasanargument.Butsincethislistenerwillonlybeusedbythisbutton,whybotherevennamingtheclass?Thecodeabove(startingwithnewandendingwithacurlybrace)definesandcreates aninstanceofananonymousclassandoverridesthemethodactionPerformed()thatprocessesa clickedevent.Looksabitconvoluted,doesn’tit? InAS3,youjustdefineafunctionandpassittothelistenerasanargumentforanexecution.For example: myButton.addEventListener(“click”, placeOrder);
HereplaceOrderisafunctionclosurethatdefinesthebusinesslogic;it’spassedtothemethodaddEventListener()andisexecutedassoonasthebuttonisclicked. Topassaclosuretoanothermethodandexecuteit,Flexcreatesadynamicobjectbehindthescenes andwrapstheclosureup.Intheexamplebelow,wedefinethefollowingfunctionclosure: gotoWebPage(theURL: String) { // open a specified Web page in a new Web browser’s window getURL(theURL, “_blank”); }
Let’spassthereferencetothistoatimerfunction.ThenextlinewillexecutethefunctiongotoWebPage()infivesecondswhilepassingitthetargetURLasanargument: setTimeout(gotoWebPage, 5000, “http://www.theriabook.com”);
Interestinglyenough,theutilityfunctionsetTimeout()isitselfaclosure.Adobe’slanguagereferencesuggestsusingtheTimerobjectinsteadforefficiencyreasons:whenyoucreateaninstance ofanobjectyourself(asopposedtothedynamicobjectsthatareusedwithclosures),thegarbage collector’sworkbecomesabiteasier.SosetTimeout()andsomeothertimingutilityfunctionsexist inAS3onlyforbackwardcompatibility/simplicityreasons.
AsynchronousProgramming TraditionalJavaprogrammershavetogetusedtothefactthatwhenaclientmakesanRPCcalltoa server,itactuallyjustrequeststheexecutionofthismethod,allowingtheusertocontinueworking withtheapplication.Itdoesn’tblockuntiltheresultcomesback.Aftersometime,whichcouldbe aslongasseveralseconds,theresultorerrormessagecancomebacktotheclient,anditneedsto rememberwhichcomponenthassentthisrequestandprocessitaccordingly.You’llseeanexample RIAWITHADOBEFLEXANDJAVA
91
CHAPTER4
ofsuchanapplicationinChapter5.Meanwhile,we’llwalkyouthroughahigh-leveloverviewofthe communicationprocess. ActionScriptdoesn’thaveanexplicitmultithreadingAPI.Itcanbeemulatedbyrunningmultiple instancesoftheplayerwithinthehostingpage,butit’snotatrivialtask.Onthebrightside,Flex makesstandardtasksthatrequiremultithreadingmuchsimpler.Insteadofexplicitmultithreading,theclassesthatneedtobeexecutedasynchronouslyusepre-builtFlashplayercomponents thathandleallthetrackingandoptimization. AfinancialnewsservicefromYahooaccessedviaanHTTPServiceisagoodexampleofasynchronouscommunication: … var token: AsyncToken = newsFeed.send({security:”ADBE”}); token.responder = new Responder(processBusinessNews, processBusinessNewsErrors);
Thelasttwolinesmeanthefollowing:requestthenewsaboutAdobe(ADBE),andexpecttheresult tocomebackasaninstanceoftheAsyncTokenobject. FlashPlayertakescareoftrackingmultiplerequestsandtheinvocationoftheresponders’methods uponcompletion.Aresponderspecifiesthatuponreturn,FlashPlayercallsprocessBusinessNews whensuccessfulandprocessBusinessNewsErrorsifitfails.TheprocessBusinessNewsfunctionis alsoknownasaneventhandlerandmustbepresentinthecode. TheprocessBusinessNewsfunctionmaylooklikethis: public function processBusinessNews(evt:Event):void { myNewsCollection.addNews(evt.result.businessnews); // code to display the news on the screen goes here }
DataBinding TheFlexDevelopersGuidestatesthat“Databindingistheprocessoftyingthedatainoneobjectto anotherobject.Itprovidesaconvenientwaytopassdataaroundinanapplication.” We’dliketoofferyouanotherdefinition:databindingistheabilityofoneobjecttowatchthechangesofaspecifiedproperty(ies)onanotherobject,expression,orafunctionthatreturnsavalue. TowatchsuchchangesinJava,you’dneedtowriteandregisterlistenersinoneobject,whileanotherobjecthastofireappropriateeventswhenthepropertyinquestionchanges.Eventhough AS3hassimilarlanguageconstructs,there’samucheasierwaytodothisinFlex. Flexoffersyouseveralwaysofbindingdatabetweentheobjectsusingaverysimplesyntaxand
92
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
thejobofcreatinglistenerswillbedoneforyouautomaticallywithouthavingtowritesuchcode manually.Youcanreviewthelistenersandothergeneratedcodebyusingthecompileroption– keep-generated-actionscript=true.
BindinginMXML Justtogiveyouaquickpeekintodatabindinginitssimplestform,lookatthesampleapplication inListing4.6thatconsistsofaTextInputfieldandalabel.Thetextofthelabelisboundtothetext oftheinputfieldbyasimpleuseofcurlybraces: text=”{myTextField.text}”
Thisline“tells”myLabel:“WatchthetextpropertyoftheobjectcalledmyTextFieldandassoonas itgetsmodified,updateyourself.” Youwon’tfindanylistenerdeclarationorfiringeventsinthecodebelow.Theveryfactthatwe’ve putthetextpropertyincurlybraceswillforcetheFlexcompilertogeneratealltherequiredAS3 listeners,eventdispatchers,andbindingautomatically.
Listing4.6BindingTextFieldAndLabel.mxml
Figure4.1Typinginatextfieldisreflectedinthelabel
This application may seem pretty useless, but we’ll give you plenty of real-world examples that demonstratethepowerofdatabindingsineverychapterinthisbook.Thesearejustsomeofthe nearestexamples: RIAWITHADOBEFLEXANDJAVA
93
CHAPTER4
• • • •
WatchingapanelstateasinListing4.13 BindingadatacollectiontotheDataGridasinListing4.28 DisplayingerrorsasinListing5.10 Implementingmaster-detailrelationsbetweencomponentsasinListing5.21
Buttounderstandtheseandotherexamples,wehavetocoversomedatabindingbasics. Ineachpairofboundobjectsthere’sasource,destination,andtriggeringevent.Inourexample myTextFieldisthesource,myLabelisthedestination,andthe“change”ofthetextinthemyTextfieldisthetriggeringevent.Youcanbindmorethantwoobjectsbycreatingaso-calledbindable propertychain:objectAisasourcefordestinationB,whichinturnisasourcefordestinationC andsoon. TospecifybindinginMXML,youcanuseeitherthecurlybracesaswedidbefore,oradedicated Tag.Thepreviousexamplecanberewrittenasfollows:
Yourprogramcanhavemultipletagsusingthesamedestinationsand/orsources.
BindingExpressions Tocomplicateourexamplealittlebit,we’lladdonemoredestination:
Nowwehaveonesourceandtwodestinations(twolabels).Butmoreimportantly,we’veincluded anexpressioninsidethecurlybraces.Ifyourunthisprogram,it’llshow4asthevalueofthesecond label.Starttypingdigitsinthetextfieldandbothlabelswillimmediatelyreflectthechangesand thevalueinthesecondlabelwillbegreaterby4.
94
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Figure4.2Onesource,twodestinations
EnteringanalphacharacterwilldisplayNaN(notanumber)astheresultofourexpressionevaluation. Onthesamenote,youcanuseanAS3functioninthebindingexpressionaslongasitreturnsa value.
What’sUndertheHood? Obviously,usingcurlybracesisjustthetipoftheiceberg.Howdoesthisbindingmagicworkinternally? TheFlexcompilergeneratesbindablewrappersinAS3foreachpropertyorexpressionthatisbeingwatched(i.e.,PropertyWatcher),andit’llgeneratealltherequiredAS3codeforeventprocessing (see the section on events later in this chapter). And of course, it’ll create an instance of an mx.binding.Bindingobjectthattieseverythingtogether. Metatags are a way of providing compilers/linkers with information that’s not part of the language.IntheJavauniverse,annotationsarepeersofAS3metatags.Fordatabinding,AS3offers themetatag[Bindable].Theinformationinthemetatagsisusedtogenerateadditionalframework code and instruct the compiler to use the generated code whenever the annotated properties/ methodsareused. Forexample,let’sseetheinnerworkingsofourTextImput/Labelbindingexample(thecodeisgeneratedbytheFlexcompiler).WestartinthesourcecodeoftheTextInputcontrolandanalyzethe declarationthere.Thisiswhatwefind: [DefaultBindingProperty(source=”text”, destination=”text”)] [DefaultTriggerEvent(“change”)] [Bindable(“textChanged”)] [NonCommittingChangeEvent(“change”)] RIAWITHADOBEFLEXANDJAVA
95
CHAPTER4
Thesebindingdeclarationscausethecompilertogeneratethefollowingdatastructures: binding = new mx.binding.Binding(this, function():String { var result:* = (myTextField.text); var stringResult:String = (result == undefined ? null : String(result)); return stringResult; }, function(_sourceFunctionReturnValue:String):void {
…
myLabel.text = _sourceFunctionReturnValue; }, “myLabel.text”); _bindings[0] = binding; watchers[1] = new mx.binding.PropertyWatcher(“myTextField”, { propertyChange: true } ); watchers[2] = new mx.binding.PropertyWatcher(“text”, { textChanged: true, change: false } ); watchers[1].addListener(bindings[0]); watchers[1].propertyGetter = propertyGetter; watchers[1].updateParent(target); watchers[2].addListener(bindings[0]); watchers[1].addChild(watchers[2]); bindings[0].uiComponentWatcher = 1; bindings[0].execute();
Whataboutwatchingnotjustthepropertiesbuttheexpressions?Considerthisone: 2+2+ Number(myTextField.text)
96
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Itworksthesameway.TheFlexcompilerautomaticallygeneratesananonymouswrapperfunction forthesourceoftheevent,whichisanexpression: function():String { var result:* = (2+2+ Number(myTextField.text)); var stringResult:String = (result == undefined ? null : String(result)); return stringResult; }
Ifyou’dliketolearnmoreaboutbindinginternalsreadthesectionintheFlexDeveloper’sGuide called“AbouttheBindingMechanism.”
BindinginActionScript Youcanputthe[Bindable]metatagaboveoneormorepropertiesofanAS3class,butifyou’dlikeall thepropertiesoftheclasstobebindable,justaddone[Bindable]metatagabovetheclassdefinition: [Bindable] public class myClass{ … }
Ifyoudon’tspecifywhicheventshouldtriggeranupdateofthedestination,FlexwillusepropertyChangebydefault.Butthismetatagallowsanothersyntaxthatletsyouspecifyanyotherapplicableeventname. Forexample,ifthevariablepriceshouldtriggersomeactionswhenthestockpricechanges,youcan defineacustomeventpriceChanged(describedlaterinthischapter)andusethefollowingsyntax: [Bindable(event=”priceChanged”)] var price:Number;
Aquickpeekatthe generatedfolderrevealsthatthe[Bindable]metatagcausesthecompilerto generateashadowclasswithgetters/settersforeachbindableproperty.Inthesettermethodthe compileraddsadispatcheventtonotifylistenersoftheobjectchange.Also,thedestinationobject automaticallygetsabinding/watcherfortherespectivesource. Insomecasesitisbeneficialtoprovidedynamicbinding. Fordetails,checkoutthedocumentationfortheclassesBindingUtilsandChangeWatcher.
BindingInsideaStringandApplicationParameters Inthislittlesectionwe’llkilltwobirdswithonestone.First,we’llshowyouhowtopassparameters RIAWITHADOBEFLEXANDJAVA
97
CHAPTER4
toaFlexapplicationfromtheHTMLwrappergeneratedbyFlexBuilder,andthenyou’llseehowto usethebindinginsideatextstring. OurnextassignmentwillbetowriteaFlexapplicationthatwillrunagainstdifferentservers(dev, uat,prod)withouthavingtorecompileSWF.Itdoesn’ttakearocketscientisttofigureoutthatthe URLoftheservershouldbepassedtoSWFasaparameter,andwe’lldothisbyusingaspecialflashVarsvariableintheHTMLwrapper.Flex’sdocumentationsuggestsincludingflashVarsparameters intheObjectandEmbedtagsandreadingthemusingApplication.application.parametersinAS3 code.Asthiswaswritten,thisdoesn’twork.Butastheancientsayinggoes,“Adobeclosesonedoor butopensanotherone.”Butfirst,let’sgetfamiliarwithFlexcode:
Listing4.7BindingWithString.mxml ThescriptportionofthiscodegetsthevaluesofparametersserverURLandport(definedbyus) usingtheApplicationobject.We’lladdthevaluesoftheseparameterstotheHTMLfileasdescribed below.ThesevaluesareboundtotheMXMLlabelasapartofthetextstring. Ifyou’llopentheHTMLfilegenerated,you’llfindtheJavaScriptfunctionAC_FL_RunContentthat includesflashVarsparametersintheformofkey-valuepairs.Forexample,inoursampleapplication itlookslikethis: “flashvars”,’historyUrl=history.htm%3F&lconid=’ + lc_id +’’
98
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
AddourparametersserverURLandporttothisstring: “flashvars”,’serverURL=MyDevelopmentServer&port=8181&historyUrl=history. htm%3F&lconid=’ + lc_id
RuntheApplicationandit’lldisplaytheURLoftheserveritconnectsto.Ifyou’dliketotestyourapplicationagainstaQAserver,justchangethevaluesoftheflashVarsparametersintheHTMLfile.
Figure4.3TheoutputofBindingWithinString.html
Wehaveonelastlittlewrinkletoironout:ifyoumanuallychangethecontentofthegeneratedHTML file,nexttimeyoucleantheprojectinFlexBuilder,itscontentwillbeoverwrittenandyou’lllosethe addedflashVarsparameters.There’sasimplesolutiontothisproblem:insteadofaddingflashVars parameterstothegeneratedHTML,addthemtothefileindex.template.htmlfromthehtml-template directory,whichFlexBuilderusestogeneratetherunanddebugversionsoftheHTMLwrapper. Ofcourse,thislittleexampledoesn’tconnecttoanyserver,butitgivesyouanideaofhowtopass theserverURL(oranyothervalue)asaFlashparameter,andhowtoassembletheURLfromamix oftextandbindings
IsDataBindingaSilverBullet? TheeaseofuseofFlexdatabindingisveryaddictive,buttherearecaseswhenusingdatabinding isn’trecommended.Forexample,ifanumberofchangesisdoneonvariouspropertiesofaclass andyouwanttodisplaythefinalstateonlywhenallthechangesarecomplete,makingeachdata itembindablewouldgenerateunnecessaryeventfiringaftereachdatachange. Theotherdrawbacktousingbindingtotietogetherpropertiesofdifferentcomponentsisthatitassumessomeknowledgeaboutthecomponentinternals.Itmakesapplicationdesignabitcomplex becauseitstaticallylinkscomponentstogetherandmakeschangesinterdependent.Italsorequires thecompilertogeneratealotofcross-referencingcodethatconsumesbothtimeandmemory.
RIAWITHADOBEFLEXANDJAVA
99
CHAPTER4
Alternativearchitectureistouselooselyboundcomponents.YoucanreadabouttheminthesectiononCustomEventsbelow.
PrograminStyleoranElevatorPitch WeusuallyrunFlextrainingforourprivateclients,butonceinawhileweteachpublicclassesfor peoplewithdifferentprogrammingbackgrounds,andeachofthemcomeswithadifferentunderstandingofhowtodothingsright. We’lltellyouastorythatmighthavehappenedinreallife,butfirst,we’llremindyouoftheold Indiantaleaboutsevenblindmenandanelephant.Oneblindmantouchedtheelephant’shead, anotheronethetail,anotherwasbythelegandeachofthemvisualizedtheelephantdifferently basedonwhathetouched. Studentsusuallyarriveintheclassroomearly,butthistimethreeseatswereempty.Fiveminutes latertheinstructorgotaphonecallfromonepersonexplainingthatthethreehadgottenstuckin theelevatorandwouldbethereforanother15minutesuntiltheservicemanarrived.Needlessto sayeachofthemhadalaptop(donotleavehomewithoutone),sotheinstructorgavethemashort assignmenttohelpthemusethetimeproductively.Here’stheassignment: Createawindowwithapanelthatcanresizeitselfwiththeclickofa+/-buttonlocatedintherighthandcornerofthepanel.Oneclickshouldminimizethepanel’sheightto20pixels,andasubsequent oneshouldmaximizeto100pixels,andsoon.Forexample,thesearethetwostatesofsuchapanel:
100
Figure4.4Twostatesofthepanel
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
FromCOBOLtoFlex ACOBOLprogrammerthoughttohimself,”Weusedtowritelongprogramsbecauseduringjob interviewstheyusuallyaskhowmanylinesofcodeIwrote.Theseguysaredifferent,sotoearn agoodgrade,thisprogramshouldbesmall.”Hefinishedtheprogramontimeandthisiswhatit lookedlike:
Listing4.8The“Cobol”version
FromJavatoFlex TheJavaprogrammerthought,“ThestandardFlexPanelclassdoesn’thaveapropertythatremembersthecurrentstateofthepanel,butFlexcomponentsareeasilyextendable,soI’llcreateadescendentofthepanelinActionScript,addaprivatestateflag(minimized),apublicsetterandgetter,andaresizefunction.Thatwaymynewpanelclasswillbereusableandself-contained.”Thisis hisreusableAS3classcalledResizableJPanel: package {
import mx.containers.Panel; public class ResizableJPanel extends Panel { // state of the panel private var isPanelMinimized:Boolean; public function get minimized():Boolean{ return isPanelMinimized; } public function set minimized(state:Boolean){ RIAWITHADOBEFLEXANDJAVA
101
CHAPTER4
}
}
}
}
isPanelMinimized=state;
public function resizeMe():void{ if (minimized){ minimized=false; height=maxHeight; } else { minimized=true; height=minHeight; }
Listing4.9The“Java”versionofthepanelclass ThisistheJavist’sMXMLcode:
102
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Listing4.10TheMXMLfromaJavastudent
FromSmalltalktoFlex TheSmalltalkguythought,“Letmeseeifthestandardpanelisadynamicclass.Ifnot,I’llextend itjusttomakeitdynamicandassignthepanel’sstateon-the-fly.Ihopetheinstructorisn’toneof thoseobject-orientedNazis.”ThisishispanelAS3classthatjustaddsadynamicbehaviortothe panel: package{
}
import mx.containers.Panel; public dynamic class ResizableSmtPanel extends Panel { }
Listing4.11Dynamicsubclassofapanel HisMXMLclasslookedlikethis:
Listing4.12.The“Smalltalk”versionofMXML Fifteenminuteslater,thethreestudentswereintheclassroom,andeachgotan“A”forthiselevator job.Andhere’stheFlexversion:
[Bindable] public var minimized:Boolean = false;
Listing4.13Theinstructor’sversionofthesolution There’sanothersimplesolutiontothisparticularassignmentandwe’llletthereadertrytofigureit out(hint:usestates). What’sthemoralofthisstory?LearnANOTHERlanguage,nomatterwhatyourcurrentbackground is.Initiallyyou’lltrytobringyourownculturetothisnewlanguage,buteventuallyyourhorizonswill broadenandyou’llbecomeabetterprogrammer.
Object-OrientedActionScript Youknowthedrill:alanguageiscalledobject-orientedifitsupportsinheritance,encapsulation,
104
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
andpolymorphism.Thefirsttwonotionscanbeeasilydefined: Inheritanceletsyoudesignaclassbyderivingitfromanexistingone.Thisfeatureallowsyouto reuseexistingcodewithoutcopyingandpasting.AS3providesthekeywordextendsfordeclaring inheritance. package com.theriabook.oop{ public class Person { var name:String; } } package com.theriabook.oop{ public class Consultant extends Person{ var dailyRate:Number; } } package com.theriabook.oop{ public class Employee extends Person{ var salary:Number; } }
Listing4.14.Theancestorandtwodescendents Encapsulationisanabilitytohideandprotectdata.AS3hasaccess-levelqualifierssuchaspublic, private,protected,andinternaltocontroltheaccessclassvariablesandmethods.BesidesJava-like public,private,protected,andpackageaccesslevels,youcanalsocreatenamespacesinAS3that willgiveyouanotherwaytocontrolaccesstopropertiesandmethods.Thischapterincludessome basicsamplesofnamespaces,andyoumaywanttoreadaboutthecomponentmanifesttilesin Chapter11. However,ifJavaenforcesanobject-orientedstyleofprogramming,thisisn’tthecasewithAS3,becauseit’sbasedonthescriptinglanguagestandard.Object-orientedpuristsmaynotlikethenext codesnippet,butthisishowaHelloWorldprogramcanlookinAS3: trace(“Hello, world”);
That’sit.Noclassdeclarationisrequiredforsuchasimpleprogram,andthedebugfunctiontrace() canliveitsownclass-independentlife,asopposedtoJava’sprintln()doublywrappedintheSystemandPrintStreamclasses.Youcanwriteyourownfunctions,attachthemtodynamicobjects, andpassthemasparameterstootherfunctions.AS3supportsaregularinheritancechainaswell asso-calledprototypeinheritancewhereyoucanaddnewpropertiestotheclassdefinitionsand RIAWITHADOBEFLEXANDJAVA
105
CHAPTER4
theybecomeavailabletoallinstancesoftheclass.Moreover,youcandisablethevalidationofthe propertiesandmethodsduringcompilationbyturningoffthe“strict”mode.InJava,behindevery objectinstancethere’sanentityofthetypeClass.Thisisnotanobjectitself,butit’sputinmemory byclassloaders.
ProgramDesignwithInterfacesandPolymorphism AsinJava,AS3interfacesarespecialentitiesthatdefinethebehavior(methods)thatcanbeimplementedbyclassesusingthekeywordimplement.Afterexplainingwhat’scrucialtoOOPinterfaces, we’lldiscusshowtowritegenericcodewithoutthem. To illustrate how you can design AS3 programs with interfaces, we’ll add some behavior to the classesfromListing4.14.Let’sworkonthefollowingassignment. Acompanyhasemployeesandconsultants.Designclassestorepresentthepeopleworkinginthis company.Theclassescanhavethefollowingmethods:changeAddress,giveDayOff,increasePay.Promotioncanmeanadayoffandasalaryraisedaspecifiedpercentage.Foremployees,theincreasePay methodshouldraisetheyearlysalaryand,forconsultants,itshouldincreasetheirhourlyrate. First,we’lladdallthecommonmethodsthatareapplicabletobothemployeesandconsultantsto thePersonclass. package com.theriabook.oop { public class Person { var name:String; public function changeAddress(address: String): String { return “New address is” + address; }
}
}
private function giveDayOff(): String { return “Class Person: Giving an extra a day off”; }
Listing4.15.TheAncestorclass:Person Inthenextstep,we’lladdanewbehaviorthatcanbereusedbymultipleclasses:theabilitytoincreasetheamountofaperson’spaycheck.Let’sdefineaPayableinterface: package com.theriabook.oop { public interface Payable
106
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications { }
function increasePay(percent:Number): String;
}
Listing4.16.InterfacePayable Morethanoneclasscanimplementthisinterface: package com.theriabook.oop { public class Employee extends Person implements Payable { public function increasePay(percent:Number):String { // Employee-specific code goes here … return “Class Employee:Increasing the salary by “+ percent + “%\n”; } } }
Listing4.17TheAS3classEmployeeimplementingthePayableinterface package com.theriabook.oop { public class Consultant extends Person
implements Payable {
public function increasePay(percent:Number): String{ // Consultant-specific code goes here … return “Class Consultant: Increasing the hourly rate by “ +
}
}
percent + “%\n”;
}
Listing4.18TheConsultantclassimplementingPayable WhentheConsultantclassdeclaresthatitimplementsaPayableinterface,it“promises”toprovideimplementationforallthemethodsdeclaredinthisinterface–inourcasethere’sjustone increasePay()method.Whyisitsoimportantthattheclass“keepsthepromise”andimplementsall theinterface’smethods?Aninterfaceisadescriptionofsomebehavior(s).InourcasethePayable behaviormeanstheexistenceofamethodwiththesignaturebooleanincreasePay(intpercent). IfanyotherclassknowsthatEmployeeimplementsPayable,itcansafelycallanymethoddeclared inthePayableinterface(seetheinterfaceexampleinthePromoterclassinListing4.19). RIAWITHADOBEFLEXANDJAVA
107
CHAPTER4
InJava,besidesmethoddeclarations,interfacescancontainfinalstaticvariables,butAS3doesn’t allowanythingintheinterfacesexceptmethoddeclarations. Interfaces are another workaround for adjusting to the absence of multiple inheritance. A class can’thavetwoindependentancestors,butitcanimplementmultipleinterfaces,itjusthastoimplementallthemethodsdeclaredinalltheinterfaces.Onewaytoimplementmultipleinheritance (that we often use but don’t recommend it) is to use an“include” statement with the complete implementationofallclassesimplementinginterface: public class Consultant extends Person implements Payable { include “payableImplementation.as” } public class Employee extends Person implements Payable { include “payableImplementation.as” }
Forexample,aConsultantclasscanbedefinedas: class Consultant extends Person implements Payable, Sueable {…}
ButifaprogramsuchasPromoter.mxml(seeListing4.19)isinterestedonlyinPayablefunctions,it cancasttheobjectonlytothoseinterfacesitintendstouse.Forexample: var var var var
emp:Employee = new Employee(); con:Consultant = new Consultant(); person1:Payable = emp as Payable; person2:Payable = con as Payable;
Nowwe’llwriteanMXMLprogramPromoterthatwillusetheEmployeeandConsultantclasses definedinListings4.17and4.18.Clickonthebuttonandit’llcreateanarraywithamixofemployees and consultants. Iterate through this array and cast it to the Payable interface, then call the increasePay()methodoneachobjectinthiscollection.
108
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Listing4.19Promoter.mxml Theoutputofthisprogramwilllooklike:
Figure4.5TheoutputofPromoter.mxml RIAWITHADOBEFLEXANDJAVA
109
CHAPTER4
Thelinep.increasePay(5)inthelistingabovemaylookalittleconfusing.HowcanwecallaconcreteincreasePaymethodonavariableofaninterfacetype?ActuallywecallamethodonaconcreteinstanceoftheEmployeeoraConsultantobject,butbycastingthisinstancetothePayable typewe’rejustlettingtheAVMknowthatwe’reonlyinterestedinmethodsthatweredeclaredin thisparticularinterface. Polymorphism–WhenyoulookatourPromoterfromListing4.19,itlookslikeitcallsthesame increasePay()methodondifferenttypesofobjects,anditgeneratesdifferentoutputsforeachtype. Thisisanexampleofpolymorphicbehavior. Intherealworld,arrayworkerswouldbepopulatedfromsomeexternaldatasource.Forexample, aprogramcouldgetaperson’sworkstatusfromthedatabaseandinstantiateanappropriateconcreteclass.TheloopinPromoter.mxmlwillremainthesameevenifweaddsomeothertypesof workersinheritedfromthePersonclass!Forexample,toaddanewcategoryofworker–aforeign contractor,we’llhavetocreateaForeignContractorclassthatimplementstheincreasePaysmethod andmightbederivedfromthePersonclass.OurPromoterwillkeepcastingalltheseobjectstothe PayabletypeduringruntimeandcalltheincreasePaymethodofthecurrentobjectfromthearray. Polymorphismallowsyoutoavoidusingswitchorifstatementswiththecheckingtypeoperator is.Belowisabad(non-polymorphic)alternativetoourloopfromPromoter.mxmlthatchecksthe typeoftheobjectandcallsthetype-specificmethodsincreaseSalary()andincreaseRate()(assumingthatthesemethodsweredefined): for(var i: int = 0; i < workers.length; i++) { var p: Person = workers[i] as Person; if (p is Employee){ increaseSalary(5); } else if (p is Consultant) { increaseRate(5); } }
Listing4.20Abadpracticeexample You’dhavetomodifythecodeaboveeachtimeyouaddanewworkertype.
PolymorphismWithoutInterfaces IfthiswasaJavabook,wecouldhavepattedourselvesonthebackforprovidinganiceexampleof polymorphism.Let’sthinkofamoregenericapproach–doweevenneedtouseinterfacestoensurethataparticularobjectinstancehasarequiredfunctionlikeincreasePay?Ofcoursenot.Java hasapowerfulintrospectionandreflectionmechanismthatanalyzeswhichmethodsexistinthe classinquestion.It’simportanttorememberthoughthatinJava,objectinstanceshaveonlythose methodsthatweredefinedintheirclasses(blueprints).ThisisnotthecasewithAS3.
110
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Thereisanotherurbanmyththatreflectionisslow,andyoushoulduseitonlyifyouhaveto.But this consideration isn’t valid in programs that run on the client’s PCs, because we don’t have to worryabouthundredsofthreadscompetingforasliceoftimeonthesameserver’sCPU(s).Using reflectionontheclientisfine.Evenontheserverproperthecombinationofreflectionwithcaching allowsyoutoavoidanyperformancepenalties. AS3providesveryshortandelegantsyntaxforintrospection,andwe’dliketospendsometimeillustratingpolymorphismwithoutthetypecastingandstrictJava-stylecoding. Let’srevisitoursampleapplication.Workersgetpayandbenefitsandvacations;consultantsare paidhourlywages.Butretireesmayhavesomeformofreceivingpensions,theboardofdirectors mightgetpaidbutnobenefits–aretheyworkers?No,they’renotandtheirobjectsmaynotnecessarilyimplementthePayableinterface,whichmeansthatthetypecastingfromListing4.19would causearuntimeexception. HowaboutraisingthecompensationofeveryPersonevenifitdoesn’timplementPayable?Ifone oftheseobjectssneaksintotheworkersarray,simplecastingtoPayableasshownbelowwillthrow anexception: Payable p = Payable(workers[i]);
Let’srewritetheloopfromListing4.19asfollows: for(var i:uint = 0; i < workers.length; i++) { var p:* = workers[i][“increasePay”]; output.text+=p==undefined?”no luck”:p(5); }
Thisshortloopdeservesanexplanation.First,we’vedeclaredavariablepoftype*.Thisdeclaration meansthatpcanbeanytype.Usinganasteriskabitmoreopenthanvarp:Object;allowsthevariableptohaveaspecialvalueofanundefinedtypeasusedintheabovecodesample. Let’sdissectthefollowingline: var p:* = worker[i][“increasePay”];
Itmeans“GetareferencetotheincreasePay()functionfromthearrayelementworkers[i].”Youmay ask,“WhyusethebracketsaroundincreasePayinsteadofthedotnotation?”Thereasonisthatdot notationwouldaskthecompilertofindandvalidatethisfunction,whilethebracketstellthecompilernottoworryaboutit,theprogramwilltakecareofthislittlesomethinginsidethebrackets duringruntime. Basically,thesinglelineaboveperformstheintrospectionandgetsapointertotheincreasePay functionforfutureexecutionintheline: RIAWITHADOBEFLEXANDJAVA
111
CHAPTER4 output.text+=p ==undefined?”no luck”:p(5);
Ifthisparticularelementoftheworkersarraydoesn’thaveincreasePaydefined(itsclassmustbe declared dynamic), add“no luck” to the text field. Otherwise executethis object’s version of increasePay,passingthenumberfiveasitsargument.Thelineaboveisstillapotentialproblemif theclassdoesn’thavetheincreasePayfunction,buthasapropertywiththesamename.Thebetter versionlookslikethis: output.text+=!(p is Function)?”no luck”:p(6);
Let’semphasizethatagain:thisincreasePaymethoddoesn’thavebedefinedinanyinterface. Javaprogrammerswouldcallthiswildanarchy.Ofcourse,adheringtostrictrulesandcontractsin Javaleadstomorepredictablecodeandlesssurprisesatruntime.ButmodernJavamovestoward dynamicscripting,addingimplicittypecasting,runtimeexceptions,etc.Overuseofinterfaces,private,protected,andother“nicecleanobject-orientedtechniques”doesn’tpromotecreativethinkinginsoftwaredevelopers.WehopeallcodesamplesinthischapterbreaktheOOPspellthatJava developersliveunder.
NamespacesinActionScript NamespacesinAS3asinMXMLareusedtolimitthescope(visibility)ofmethods,properties,or constants.They’realsousedtoavoidnamingconflictsincaseswhereyoucreateyourowncustom components that may have the same names as the Flex Framework or other vendor’s counterparts. You can think of access control keywords – public, private, protected, and internal – as built-in namespaces.Ifamethodhasbeendeclaredas protected calculateTax(){}
youcansaythatthecalculateTax()methodhasaprotectednamespace.ButAS3letsyoudefineyour ownnamespacestouseinsteadofthesestandardlanguagequalifiers. Tointroduceyourownnamespace,youneedtotakethefollowingsteps: • • •
Declareanamespace Applythenamespace Referencethenamespace
Let’swriteasimpleprogramforanaccountantwhocalculatestaxes,butcustomerswhobelong totheMafiawouldpayonlyhalftheamount.Todothis,we’llstartbydeclaringtwonamespaces: regularandriabook.
112
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications package com.theriabook.namespaces { public namespace mafia=”http://www.theriabook.com/namespaces”; }
Listing4.21riabook.as PleasenotethatusingaURIinthenamespacedeclarationisoptional.Thelistingbelowdoesn’tuse anyexplicitURI,butthecompilerwillgenerateone. package com.theriabook.namespaces { public namespace regular; }
Listing4.22regular.as Toapplythenamespaces,we’lldefineaTaxclasswithtwocalcTax()methodsthatwilldifferintheir namespaceaccessattributesandtheamountoftax“calculated”: package com.theriabook.tax{ import com.theriabook.namespaces.*; public class Tax { regular static function calcTax():Number{ return 3500; } riabook static function calcTax():Number{ return 1750; } } }
Listing4.23TheAS3classTax package com.theriabook.test { import com.theriabook.namespaces.*; import com.theriabook.tax.Tax; import mx.controls.Alert; use namespace regular; // use namespace mafia; public class TestTax { public static function myTax():void { RIAWITHADOBEFLEXANDJAVA
113
CHAPTER4
}
}
}
var tax:Number; tax=Tax.calcTax(); Alert.show(“Your tax is “+ tax,”Calculation complete”);
Listing4.24TestTax.as Sinceweapplythenamespacefortheregularcustomer,s/hewillhavetopayataxof$3,500.The MXMLcodethatusesTestTaxisshownbelow:
Listing4.25TestTax.mxml TheoutputofthisprogramlookslikeFigure4.6.Switchtoanothernamespacebychangingtheuse statementtolooklike use namespace riabook;
andtheamountofthetaxtobepaidwillbesubstantiallylower.Besidesthedirectiveusethataffectstheentireblockofcode,AS3permitsfiner-grainednotationtorefertoaspecificnamespace withanamequalifier(adoublecolon).Inourexample,thismaylooklike: tax = Tax.riabook::calcTax();
114
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Figure4.6TheoutputoftheTextTax.mxmlforregularcustomers
Usingnamespacesprovidesanadditionalmeansofvisibilitycontrol.Themethods,classpropertiesoftheconstants,canbephysicallylocatedindifferentpackages,butmarkedwiththesame namespacequalifier,andaone-linenamespacechangecanengageacompletelydifferentsetof methods/propertiesacrosstheentireapplication.
UsingFlexwithJavaServerPages InChapter5,we’lluseFlexDataServicestoconnectaFlexclientwithplainoldJavaobjects(POJO) on the server using the object . FDS is great software, but you may already havesomeWebapplicationswritteninanothertechnologyandjustwanttoputaprettyFlashPlayerfaceonyourexistingJavaWebapplicationsthatuseJavaServerPages(JSP).Sothenextcouple ofpageswillshowyouhowto“teach”FlashPlayertocommunicatewithaJSPwithouthavingto useFDS.
RetrievingDatafromJSP We’llbeusingJSPhere,butyoucanreplaceJSPwithanytechnologyyou’recomfortablewith:servlets,ActiveServerPages,Python,PHP,etal.WhatevercanspitoutthedatatoaWebbrowsershould workthesameway. We’llshowyouareallysimpleapplicationwritteninFlex2thattalkstoaJSPthatgeneratesXML withtheinformationaboutemployees:
Alex Olson 22java, HTML, SQL RIAWITHADOBEFLEXANDJAVA
115
CHAPTER4
...
Listing4.26AfragmentoftheXMLemployeesdata Let’sjusthardcodethisXML(we’vegotthreepersons)intoaJSPthatconsistsofoneout.println() statement,wheretheXMLgoesbetweenthedoublequotes:
ThecompleteJSPlookslikethis(justputyourXMLinonelinesoyouwon’tbebotheredwith stringconcatenations):
Listing4.27employees.jsp DeploythisJSPundersomeservletcontainer.InthepopularApacheTomcatthismeanstosave it as employees.jsp under the webapp\test directory. As a sanity check we make sure that we’ve deployedthisJSPcorrectly:enteringhttp://localhost:8080/test/employees.jspintheWebbrowser hastoreturntheemployeedata.NowopenFlexBuilderandcreatetheapplication:
116
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Listing4.28DataGrid_E4X_JSP.mxml ThiscodeusesthecomponentthatletsyouconnecttoaspecifiedURLeither directlyorthroughaproxy.TheHttpServiceobjectisdesignedtocommunicatewithanyURIthat understandsHTTPrequestsandresponses.Inthecodeabovewe’vejustspecifiedtheURLforthe JSPfromListing4.24.Thedataproviderofourdatagridusesbinding(seethecurlybraces)and E4XsyntaxtoparsetheXMLandpopulatethistablewiththeelementslocatedunderthe XMLtagthat’scomingfromouremployees.jsp.Inthenextsectionwe’llexplainFlexdatabinding inmoredetail. OntheapplicationCompleteevent,thecodeemployees.send()makesanHTTPrequesttotheURL specifiedintheHTTPService,andourJSPreadilyreturnstheXMLthat’sboundtothedatagrid. Compileandrunthisprogram,andit’llshowyouthefollowing:
Figure4.7TheoutputofDataGrid_E4X_JSP.mxml
KeepinmindthatsuchadirectconnectionfromHTTPServicetoaJSPisonlypermittedifyour JSPandFlexapplicationarecomingfromthesamedomain,oriftheWebserveryou’reconnecting tohasthecrossdomain.xmlfilespecifyingacross-domainconnectionpolicywiththeappropriate permissionforyoursoralldomains.Youcanreadmoreaboutconfiguringcrossdomain.xmlinthe productmanualunder“BuildingandDeployingFlex2Applications.”
RIAWITHADOBEFLEXANDJAVA
117
CHAPTER4
SendingDatafromFlextoJSP InthenextversionofourFlex-JSPapplicationwe’llshowyouhowtopostdatafromaFlexformto JSP.We’llputasimpleformunderthedatagridabovetoenterthedataaboutthenewemployeeas inFigure4.8.PressingtheAddEmployeebuttonwillsubmittheentereddatatotheJSP,whichwill attachthemtoexistingemployeesandreturnbacksothedatagridcanberepopulatedtoinclude thenewlyinsertedemployee. Todesigntheform,we’llbeusingtheFlexobjectscontainer,whichdiffersfromthe HTMLtag.Thelatterisaninvisiblecontainerthatholdssomedata,whileis usedtoarrangetheinputcontrolsonthescreenwiththeirlabels.We’llalsouseto storethedataboundtoour.Let’salsomaketheemployee’snamearequiredfieldand addaso-calledvalidatortopreventtheuserfromsubmittingtheformwithoutenteringthename. Itwilllooklike:
{empName.text} {age.text} {skills.text}
Listing4.29Theemployeeentryformanditsmodel Therequired=trueattributedisplaysaredasteriskbytherequiredfieldbutdoesn’tdoanyvalidation.Thedisplaystheprompt“Thisfieldisrequired”andmakestheborder oftherequiredfieldredifyoumovethecursoroutofthenamefieldwhileit’sempty,andshowsa promptwhenyoureturntothisfieldagainasinFigure4.8.Butwe’dliketoturnthisdefaultvalidationoffbyaddingthetriggerEventpropertywithablankvalue:
118
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
We’llalsoaddourownAS3validateEmpName()function.NowtheclickeventoftheAddEmployee buttonwillcallvalidateName(),whichinturnwilleithercallthesubmitForm()functionifthename wasenteredordisplayamessagebox“Employeenamecannotbeblank”. Validatorsareoutsidethescopeofthischapter,andsowe’lljustmentionthatFlexcomeswitha numberofpre-definedclassesthatderivefromthebaseclassValidator.Theyensurethattheinput datameetcertainrules.Thenamesoftheseclassesareself-explanatory:DateValidator,EmailValidator, PhoneNumberValidater, NumberValidator, RegExValidator, CreditCardValidator, ZipCodeValidator,andStringValidator.Thesevalidatorsworkontheclientside,andround-tripstotheserveraren’trequired.Aprograminitiatesthevalidationprocesseitherasaresponsetoaneventorbya directcalltothemethodvalidate()oftheappropriatevalidatorinstanceasinListing4.30 ThefinalversionoftheFlexportionofourapplicationisshownbelow.
{empName.text} {age.text} {skills.text}
RIAWITHADOBEFLEXANDJAVA
119
CHAPTER4
Listing4.35GasStation1.mxml PleasenotethatthecomboboxcbMsgTypesispopulatedfromamessageTypearraythatismarked [Bindable]andwillbeusedbelowforfilteringthemessagesinthedatagrid.Also,sincewedidn’t definethePaidBydatagridcolumninthisversionofthegasstationapplication,thecorresponding datafromthedataprovideraren’tshown.
Figure4.12RunningGasStation1
RIAWITHADOBEFLEXANDJAVA
129
CHAPTER4
AddingXMLListCollection FlexcollectionclassesimplementtheIlistandICollectionViewinterfacesthatletyouadd,remove, andupdateitemsinacollection.Theseinterfacesalsohavemethodsfordispatchingeventswhen thedataintheunderlyingcollectionchange.Thisbecomeshandywhenyouuseacollectionasa dataproviderofoneofthelist-basedcontrols–justaddanewelementtosuchcollectionandthe datainthesecontrolsautomaticallyreflectthechange. Using collections (see the mx.collections package) as data providers is well described at http:// www.adobe.com/devnet/flex/quickstart/using_data_providers/. We’ll just show you one of the waystodealwithcollectionsinourgasstationapplication. Basicallywe’lladdamiddlemanbetweentheXMLobjectandthedatagrid.Nowthedatagrid’s providerwillbecomeanXMLListCollection(builtontopofXMLactivities):
Justrecompileandruntheapplicationagain–itwilldisplaythesamewindowasinFigure4.11.
Filtering Thenextstepistoallowtheusertofilterthedatabyoctane(thecheckboxes)ormessagetype(the combobox).We’lladdaninit()functionthatwillbecalledontheapplicationCompleteevent,when alltheobjectsareconstructedtoassignthefilterMessages()filterfunctiontothecollectiontodo thefiltering: msgList.filterFunction=filterMessages;
Theactualfilteringwillhappenwhenwecalltherefresh()functiononthecollection.
// some code is omitted here private function init():void { // assign the filter function msgList.filterFunction=filterMessages; // perform filtering msgList.refresh(); } private function filterMessages(item:Object):Boolean{ // Check every checkbox and the combobox and
130
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications // populate the datagrid with rows that match // selected criteria if (item.octane==”87” && this.cbx87.selected) return true; if (item.octane==”89” && this.cbx89.selected) return true; if (item.octane==”93” && this.cbx93.selected) return true; }
return false;
Ifyouneedtoremovethefilter,justsetthefilterFunctionpropertytonull. Runtheapplicationaftermakingthesechangesandyou’llseeanemptytableonthescreen.When thecreationoftheapplicationwascomplete,FlashVMcalledtheinitmethod,whichassignedthe filterfunctiontoourXMLListCollection,andcalledrefresh(),whichappliedthisfiltertoeachXML nodeofourcollection.Sincenoneofthecheckboxeswasselected,thefilterMessagesfunctioncorrectlyreturnedfalsetoeachnodeleavingthedatagridempty.Tofixthis,let’smakeaslightchange inthecheckboxessothey’llbecheckedoffduringcreation.
Nowtheprogramwillshowalltherowsagain.Trytounchecktheboxes–nothinghappensbecause theapplicationdoesn’tknowthatitneedstoreapplythefilterfunctiontothemsgListagain.Thisis aneasyfix–let’srefreshthemsgListoneachclickonthecheckbox:
Thefilteringbyoctanenumberworksfine.Addingthecodesnippetbelowtothebeginningofthe filterMessages() function will engage the filtering by message type according to the combo box selection: if (cbMsgTypes.selectedLabel !=”all” && item.@msgType!=cbMsgTypes.selectedLabel ){ return false; }
RIAWITHADOBEFLEXANDJAVA
131
CHAPTER4
Master-DetailRelationships Wetookcareofthebasicfunctionalityofthedatagridcontrol.Sincetheturnoverrateatgasstationsisprettyhigh,let’saddsomehelptonewemployeesbypopulatingtheRequiredActionstext areabasedontheselectedmessagetype.Thisisatypicalmaster-detailrelationshipstask,where thedatagridwithmessagesis“themaster”andthetextboxshowsthedetails. We’llstartbycreatinganactions.xmlfilewherewestoretherecommendedactionsforeachmessagetype.
Sale of gasoline products Sale is good news. No action required
Purchase of gasoline products from suppliers If the gas station owner is not on premises, please call him at 212123-4567. Otherwise no actions is required
Spill of gasoline products on the ground Get a bucket of sand and cover the mess. Expect to receive smaller pay check this week.
Listing4.36MessageTypes.xml To read and parse this file into an XML object, we just have to write one line (thanks to E4X):
Thenextstepistospecifythataselectionofadifferentrowinthedatagridshouldcallthefunction thatfindsanddisplaystheappropriatemessagefromMessageTypes.xml.Andagain,E4Xmakes thisjobabreeze: private function getAction():void { txtAction.text= msgTypes.message.(@type==messageBook.selectedItem.@msgType).actions; }
132
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Theexpression msgTypes.message.(@type==messageBook.selectedItem.@msgType )
means select the XML element that has an attribute type that is the same as in the selectedrowinthe@msgTypecolumninthedatagrid.WhenthisXMLelementisidentified,we assignitsvaluetothetxtActiontextarea. Aswesaidearlier,changingtheselectedrowinthedatagridshouldinitiatethegetAction()functioncall.Let’smodifythedeclarationofthedatagridandaddthechangeeventprocessing:
Compileandrunthisprogram,selectarowinthedatagrid,andtheactiontextboxwillbepopulated:
Figure4.13Populatingtherequiredactionsfield
RIAWITHADOBEFLEXANDJAVA
133
CHAPTER4
We’realmostthere.Whyalmost?Becauseiftheuserstartsfilteringthedatabyoctaneoramessage type,theactiontextfieldwon’tbecleaned.Tofixthis,let’screatearefreshData()functionthatwill notonlyrefreshtheXMLListCollection,butalsocleanthetextfield: private function refreshData():void{ msgList.refresh(); txtAction.text=””; }
Don’tforgettoreplaceallcallstomsgList.refresh()withrefreshData().
AddingaDataFeed Intherealworld,allthemessagesshouldbepushedtoourapplicationbysomekindofmessaging program.Anotherpossibilityistohavethegasstationfrontendpollthedataatsomespecified intervalbysomekindofaserver-sideprogramthatcanbewritteninJSP,ASP,PHP,orwhateverelse canbakeanHTTPResponse.Incomingchaptersofthisbook,you’lllearnaboutvariouswaysto communicatewithremoteprograms.Butatthispoint,forsimplicity’ssake,we’llemulatearealtimedatafeedbyusingarandom-numbergeneratorandatimerthatwilladditemstoourmsgList collection at specified time intervals. Since the data collection will be constantly receiving new data,theoutputwindowshouldreflectthisbyaddingnewrowstothedatagrid. Ifthespeedofyourdatafeediscrucial,don’tpassthedataasXML,andconsiderusingArrayCollectionforstoringdatainsteadofXMLListCollection. Here’sthefinalcodefortheapplication:
refreshData()
134
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
{transID} {octane} {price} {gals} {payType} ; // adding new messages always on top activities.insertChildBefore( activities.message[0], newNode); }
]]>
Listing4.37GasStation3.mxml
RIAWITHADOBEFLEXANDJAVA
137
CHAPTER4
We’vechosenanXMLdatafeedinthisapplicationjusttointroducethereadertotheeaseofXML parsingwithE4X.Inthiscase,thebetterperformingsolutionwouldbetomovethedatafromXML toanAS3datatransferobjectanduseArrayCollectioninsteadofXMLListCollection.ThisAS3objectshoulddefinegettersthatprovidedatatoallthedatagridcolumns,includingthecalculated amountforthePaidcolumn.KeepingcalculationsinthelabelFunctionpaid()isnotagoodidea becausethelabelfunctioniscalledforeachvisiblerowwhenthenewXMLelementisinserted into the underlying XML collection. Flash Player repaints each visible data grid row when each newgastransactionisinserted,whichmeansthatthepaidamountsforeachvisiblerowwillbe recalculated. WhileusingXMLandE4Xmaylookveryattractive,youshouldn’tforgetthatwhenyou’recreating yourownAS3classes,there’slessdatatopushoverthewire. All of the code for our gas station application fits in three pages of this book.You can create simpleprototypeapplicationsinFlexwitharelativelysmallnumberoflinesofcode,butlet’snot fool ourselves: making efficient real-world applications still requires of programming in good oldJava.
Events Inobject-orientedlanguages,ifobjectAneedstonotifyobjectBaboutsomeimportantevent,it’s doneusingaso-calledObserverdesignpattern;JavaimplementsthispatternintheObserverinterfaceandObservableclass.Anobservableobjectmayhavesomethinginterestinggoingon,and otherobjectsthatwanttoknowaboutthisimplementtheObserverinterfaceandregisterthemselveswiththeobservableclass. AS3implementsthisdesignpatternusinganeventmodel.Objectscantriggereventstoeachother. SystemeventsaretriggeredbytheAVM,whileothersaretriggeredasaresultofuserinteractions with your application, such as a click on a button or a mouse move. Below are some situations wheneventscanbedispatched(triggered): •
• •
•
WhenFlashPlayerfinishescreatingacomponent,itdispatchesacreationCompleteevent. TheDisplayObjectclassisasubclassoftheEventDispatcherclass,whichmeansthateachof thedisplayobjectscanregisteraneventlistenerandprocesseventsastheyoccur. User-definedclassescandispatcheventsaccordingtothebusinesslogicoftheapplication. TheEnterFrameeventistriggeredbytheAVMattheapplication’sframerate.Inmovie-type applicationsit’sbeingdispatchedwhentheplayheadisenteringanewframe.Eventhough Flexapplicationsdon’thavemultipleframes,thiseventiscontinuouslydispatched.This makesitagoodcandidatetocheckifsomeimportantactionhasoccurred,i.e.,aBooleanflag indicatingthestatusofsomebusinesstransactionissettotrue. Theobjectsofyourapplicationcansendcustomeventstoeachother.
InanMXMLapplicationyou’djustspecifythenameoftheeventanditshandlerfunction(orinline code) in the attribute of the component without worrying too much about the underlying
138
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
AS3code.ButallFlexeventsaresubclassesofflash.events.Event,andinAS3programsyoushould registeraneventlistenerto“listen”tothiseventandwriteafunctiontohandlethiseventwhenit arrives.Forexample,thenextcodesnippetspecifiesthatFlashPlayerhastocallamethodonEnteringFrame(writtenbyyou)oneachEnterFrameevent: addEventListener(Event.ENTERFRAME, onEnteringFrame);
InJava,eventlistenersareobjects,butinAS3onlyfunctionsormethodscanlistentotheevents. IfyouneedtotriggeraneventfromtheActionScriptclass,ithastobeeitherasubclassofEventDispatcherorimplementtheIEventDispatcherinterface.ThelatteristhemorepreferredwaybecauseAS3doesn’tsupportmultipleinheritance,andyourclassmayneedtoextendanotherclass. Theotherreasontousealighterinterfaceisthatyoumaynotneedallthefunctionalitythatwas definedintheEventDispatcherclassandimplementtherequiredinterfacemethodasyouseefit. Forexample: class
MyClass extends HisClass implements IEventDispatcher{
}
EventFlow Whentheeventlistenercallsaneventhandlerfunction,itreceivesaneventobjectasaparameter, whichcontainsvariousattributesoftheevent,andthemostimportantoneistheevent’starget. ThisterminologymightbealittleconfusingtoJavadeveloperssincetheyrefertothecomponent thatgeneratesaneventasaneventsource.Butherewesaythatalleventsaregeneratedbythe FlashPlayer.Theyareinitiatedatthestagelevelandflowtothetargetcomponent(capturingthe stage),i.e.,Button,Shape,etc..Afterreachingthetarget,theevent“bubbles”itswaythroughtothe parents.So,whenyouclickonabutton,wecansaythatabuttonistheeventtarget.Ifabuttonis locatedonapanel,thiseventwillflowfromthestagetothepanelandthentothebutton–and thenallthewayback. FlashPlayer9implementsaneventmodelbasedontheWorldWideWebConsortium’s(W3C)specification entitled Document Object Model Events available at http://www.w3.org/TR/DOM-Level3-Events/events.html.Accordingtothisdocument,thelifecycleofaneventthatdealswithdisplay objectsconsistsofthreephases:capture,target,andbubbling. •
•
Capture:Duringthisphase,FlashPlayermakesafirstpasstocheckeveryobjectfromtheroot ofthedisplaylisttothetargetcomponenttoseeifanyparentcomponentmightbeinterested inprocessingthisevent.Bydefault,eventsareignoredbytheparentsofthetargetcomponent atthecapturephase. Target:Atthisphase,eventobjectpropertiesaresetforthetargetandallregisteredevent listenersforthistargetwillgetthisevent. RIAWITHADOBEFLEXANDJAVA
139
CHAPTER4
•
Bubbling:Finally,theeventflowsbackfromthetargetcomponentallthewayuptotheroot tonotifyallinterestedpartiesidentifiedduringthecapturephase.NotalleventshaveabubblingphaseandyoushouldconsulttheAS3languagereferencefortheeventsyou’reinterestedin.
Thethreeeventphasesdescribedabovedon’tapplytotheuser-definedeventsbecauseFlashPlayer 9doesn’tknowaboutparent-childrelationsbetweenuser-definedeventobjects.ButAS3developerscancreatecustomeventdispatchers,iftheywanttoarrangeeventprocessinginthreephases.
EventPropagationorBubbling ConsiderthefollowingsampleMXMLcodewithabuttoninapanel.
Listing4.38Events.mxml Runthissimpleprograminthedebugmodetoenabletraceandit’llshowyouthefollowingoutput:
Figure4.14OutputoftheEvents.mxml
ClickingonthebuttonintheFlexBuilder’sconsolewillshowthefollowing:
140
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications click event in the Button click event in the Panel
Thisillustrateseventspropagationorbubbling:theclickeventbubblesupfromthetarget(button) toitsparent(panel). Nowlet’screateanotherversionofthisapplication,wherethebuttonandthepanelaswellasthe eventprocessingarecodedinAS3.Thiswayyou’llseeandappreciateallthelegworkthatMXML didforus:
Listing4.39CreatingabuttonandapanelinActionScript
RIAWITHADOBEFLEXANDJAVA
141
CHAPTER4
Ifyourunthiscode,theoutputwilllooklikethis:
Figure4.15Abuttonandapanelwithoutnesting
Thisisnotexactlytheresultwewerelookingforandthereasonissimple:boththepanelandthe buttonwereaddedtotheapplication’sdisplaylistindependently.Let’sfixthisbyaddingthebutton tothepanelbyreplacingaddChild(myButton)withmyPanel.addChild(myButton). Nowthehierarchyofthenodesinthedisplaylistwillbedifferent,andthenoderepresentingthe buttonwillbecreatedundertheparentnodeofthepanelasshowninFigure4.16.
Figure4.16Abuttonandapanelwithnesting
Thisismuchbetter,butstillnotexactlythesameasFigure4.14.Let’strytosetthecoordinateofthe
142
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
buttontovaluesfromListing4.38: myButton.x=60.5; myButton.y=38;
Itdidnothelpbecause,bydefault,thepanelcontainerusestheverticallayout(seeChapter3)and ignorestheabsolutecoordinates.Let’saddonelinetochangeitintoanabsolutelayout: myPanel.layout=”absolute”;
Nowifyouruntheapplicationit’lllookthesameasinFigure4.13. We’vewrittenalotofActionScriptcode,andwehaven’tevenprocessedtheclickeventsyet!Westill needtoaddeventlistenersandeventhandlerstothebuttonandthepanel:
Listing4.40AS_Bubbling.mxml RunthisapplicationindebugmodeandtheconsolescreenwilllookthesameastheMXMLversionofourapplication: click event in the Button click event in the Panel
Theorderofthesemessagesisaclearindicationthatthetargeteventwasprocessedfirstandthe panelrespondedinthebubblingphase. Typically, during the capture stage event, listeners on the parent components aren’t called, but there’saversionoftheaddEventListener()methodthatcanrequestcallingthelistenersduringthe capturephase.Toturnoneventhandlingduringthecapturephase,youshouldusethethree-argumentsversionoftheaddEventListener()functioninthepanel: myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true);
Whenthethirdargumentequalstrue,ittellstheFlashPlayerthatweareregisteringthisparticular listenerforthecapturephase(there’snowaytodothisinMXML).Nowruntheapplicationthrough thedebugmodeagainandyou’llseethatthepanelrespondsfirst,thenthetargetbutton.There’s noeventprocessinginthebubblingphase. click event in the Panel click event in the Button
144
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Ifyou’dliketoprocessparenteventsduringboththecaptureaswellasbubblingphase,registertwo listenersforthepanel–onewiththethreeargumentsandonewithtwo.Theselisteners myButton.addEventListener(MouseEvent.CLICK, buttonClickHandler); myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true); myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);
willproducetheoutputprovingthatthepanelhasprocessedthebuttonclickeventtwice:during boththecaptureandbubblingphases. click event in the Panel click event in the Button click event in the Panel
Let’smakeachangeinthepaneleventhandlertoshowhowyoucanpreventtheeventfrombeing deliveredtothetargetifsomethingbadhasoccurred(fromabusinessapplicationperspective): private function panelClickHandler(event:MouseEvent) :void{ var badNews: Boolean = true; // a flag to emulate a bad situation if (event.eventPhase == EventPhase.CAPTURING_PHASE){ trace (“Capturing phase: click event in the Panel”); if (badNews){ trace (“Capturing phase: Bad news. Will not propagate click to But ton”); event.stopPropagation(); } }else { trace (“click event in the Panel”); } }
ThestopPropagation()methodcanbecalledatanyphaseoftheeventflow.Theline myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);
means“listentotheclickevent,andwhenit’lloccur,callthefunctionpanelClickHandler.” HandlingeventswassomucheasierinMXML… WhydoweevenneedtoknowabouttheseaddEventListener()functioncalls?Well,first,thereare someclassesthatdon’thaveequivalentsinMXML,henceyoudon’thaveachoice.Second,ifyour programisaddingcomponentsdynamically,addListener()isyouronlychoicesincethere’snowayto useMXMLnotationthere.Andthird,youmaypreferwritingyourcomponentsonlyinAS3. RIAWITHADOBEFLEXANDJAVA
145
CHAPTER4
CustomEvents WhileFlexcomponentscomewiththeirpre-definedevents,developerscancreatecustomevents specifictotheirapplications.Event-drivenprogrammingisaveryimportantdesignconceptbecauseitallowsanapplicationtoreacttouserinteractionwithoutimposingapre-defined“flow” ontheenduser.Itmeansthatunlikeback-endprocessesthattendtohavea“singletrackofmind” design,front-endapplicationshavetoreacttowhatoftenseemslikeanunrelatedsequenceofenduseractions.Fortunately,theevent-drivenmodeloffersanexcellentarchitectureforsuchinteractionbasedonlooselycoupledcomponentsconsumingandthrowingtheevents. Thissimplymeansaproperlydesignedcomponentknowshowtoperformsomefunctionalityandnotifiestheoutsideworldbybroadcastingoneormorecustomevents.Weneedtostressthatsuchacomponentdoesn’tsendtheseeventstoanyothercomponent(s).Itjustbroadcastsits“excitingnews”to theeventdispatcher.Ifanyothercomponentisinterestedinprocessingthisevent,itmustregisteralistenerforthisevent.Unlikedirectcomponent-to-componentcallsviapublicinterfaces,thisapproach letsyouaddprocessingcomponentsandsetupprioritieswithoutaffectingtheworkingcomponents. Beforeexplaininghowtocreateanevent-drivencomponent,we’llstatehowtousethem.Thisisa typicalscenario:MyApplicationusesComponent1andComponent2.Thecomponentsdon’tknow abouteachother.Anyevent-handlingcomponenthastodefinethiseventinside. Forexample,Component1dispatchesthiscustomeventandsendsoutaninstanceoftheevent object, which may or may not carry some additional component-specific data. MyApplication handlesthiscustomeventand,ifneeded,communicateswithComponent2withorwithoutfeedingitwithdatabasedontheresultsofthefirstcomponentevent. We’llcreateanewshoppingcartapplicationthatwillincludeamainfileandtwocomponents:the first,alargegreenbutton,andthesecond,aredTextAreafield.We’llcreatetwoseparatedirectories, “controls”and“cart,”respectively,whereourcomponentswilllive. TocreateourfirstMXMLcomponentinFlexBuilder,selectthe“controls”directoryandclickon themenuFile|NewMXMLComponent.Inthepop-upscreenwe’llenterLargeGreenButtonasa componentnameandwe’llpickButtonfromadropdownasabaseclassforourcomponent. FlexBuilderwillgeneratethefollowingcode:
Next,we’llmakethisbuttonlargeandgreenwithroundedcorners(justtogiveitaWeb2.0look). This component will dispatch an event named greenClickEvent. But when?You’ve got it: when someoneclicksonlargeandgreen.
146
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
CustomeventsinMXMLareannotatedwithinthemetadatatagtobevisibletoMXML.InListing 4.41wedeclaredacustomeventofthegenericflash.events.Eventtypeinthemetadatatag.Sincethe purposeofthiscomponentistonotifythesiblingobjectsthatsomeonehasclickedthebutton,we’ll definethegreenClickEventHandler()eventhandlertocreateanddispatchourcustomevent.
[Event(name=”addItemEvent”, type=”flash.events.Event”)]
Listing4.41LargeGreenButton.mxml PleasenotethattheLargeGreenButtoncomponenthasnoideawhatwillprocessitsaddItemEvent. It’snoneofitsbusiness–loosecouplinginaction! Indynamiclanguagesthefollowingnamingconventionsarecommonpractice:toaddan“Event” suffixtoeachofthecustomeventsyoudeclare,anda“Handler”suffixtoeachoftheevent-handler functions. Here’stheapplicationthatwillusetheLargeGreenButtoncomponent:
Listing4.42EventApplication.mxml Wehavedefinedanextranamespace“ctrl”heretomakethecontentofthe“controls”directoryvisibletothisapplication.Runthisapplicationindebugmode,andit’lldisplaythewindowinFigure 4.17.Whenyouclickonthegreenbuttonitwilloutputthefollowingontheconsole: Ouch!Igotclicked!Letmetellthistotheworld.SomeoneclickedontheLargeGreenButton. While adding attributes to , please note that code hints work and Flex BuilderproperlydisplaysthegreenClickEventinthelistofavailableeventsunderthenewcustom componentbutton.
Figure4.17TheoutputofGreenApplication.xmxl
OurnextcomponentwillbecalledBlindShoppingCart.Thistimewe’llcreateacomponentinthe “cart”directorybasedontheTextArea:
148
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Listing4.43BlindShoppingCart.mxml NotethattheBlindShoppingCartcomponentdoesn’texposeanypublicpropertiesormethodsto theoutsideworld.It’sablackbox.Theonlywayforothercomponentstoaddsomethingtothe cartisbydispatchingtheaddItemEventevent.Thenextquestionishowtomapthiseventtothe functionthatwillprocessit.WhensomeoneinstantiatestheBlindShoppingCart,FlashPlayerwill dispatchthecreationCompleteeventonthecomponentandourcodewillcalltheinit()private methodthataddstheaddItemEventeventlistenermappingtotheaddItemToCartEventHandler function.Thisfunctionjustappendsthetext“Yes!Someonehasput…”toitsredTextArea. The RedAndGreenApplication application uses the LargeGreenButton and BlindShoppingCart components.
Listing4.44RedAndGreenApplication.mxml
RIAWITHADOBEFLEXANDJAVA
149
CHAPTER4
Let’sGoThroughtheSequenceofEvents Whenthegreenbuttonisclicked,thegreenButtonHandleriscalledanditcreatesanddispatches theaddItemEventeventbacktoitself.Theeventbubblestotheparentcontainer(s),notifyingall listeningpartiesoftheevent.TheBlindShoppingCartlistensforsuchaneventandrespondsby addingtext.Runthisapplication,clickonthebutton,andthewindowshouldlooklikethis:
Figure4.18TheoutputofRedAndGreenApplication.mxml
Nowonemoretime:thegreenbuttoncomponentshootstheeventtotheoutsideworldwithout knowinganythingaboutit.Thatisverydifferentfromthecasewhenwewouldwrite“glue”code likecart.addEventListener(“click”,applicationResponseMethodDoingSomethingInsideTheCart).
SendingDataUsingCustomEvents Tomakeourblindshoppingcartmoreuseful,wehavetobeablenotonlytofireacustomevent, buthavethiseventdeliveradescriptionoftheitemthatwaspassedtoshoppingcart.Todothis, we’llhavetocreateacustomeventclasswithanattributethatwillstoreapplication-specificdata. Thisclasshastoextendflash.events.Event;overrideitsmethodclonetosupporteventbubbling; andcalltheconstructorofthesuper-class,passingthetypeoftheeventasaparameter.TheAS3 classbelowdefinesaitemDescriptionpropertythatwillstoretheapplication-specificdata. package cart { import flash.events.Event; public class ItemAddedEvent extends Event { var itemDescription:String; //an item to be added to the cart public static const ITEMADDEDEVENT:String =”ItemAddedEvent”; public function ItemAddedEvent(description:String ) { super(ITEMADDEDEVENT,true, true); //bubble by default
150
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
}
}
}
itemDescription=description; override public function clone():Event{ return new ItemAddedEvent(itemDescription); }
//
bubbling support inside
Listing4.45ThecustomeventItemAddedEvent The new version of the shopping cart component is called ShoppingCart and its event handler extractstheitemDescriptionfromthereceivedeventandaddsittothetextarea.
Listing4.46ShoppingCart.mxml There’sadesignpatterncalledInversionofControlorDependencyInjection,whichmeansthatan objectdoesn’taskotherobjectsforrequiredvalues,butassumesthatsomeonewillprovidetherequiredvaluesfromoutside.ThisisalsoknownastheHollywoodprincipleor”Don’tcallme,I’llcall you.”OurShoppingCartdoesexactlythis–itwaitsuntilsomeunknownobjecttriggersanevent itlistenstothatcarriesanitemdescription.Ourcomponentknowswhattodowithit,i.e.,display intheredtextarea,validateitagainsttheinventory,senditovertotheshippingdepartment,and soon. Next,we’llcompletelyreworkourLargeGreenButtonclassintoaNewItemcomponenttoincludea labelandatextfieldtoentersomeitemdescriptionandthesameoldgreenbutton:
RIAWITHADOBEFLEXANDJAVA
151
CHAPTER4
[Event(name=”addItemEvent”, type=”flash.events.Event”)]
WhenwelookatournewapplicationwithitsnewShoppingCartandNewItemcomponents,it’s almostindistinguishablefromtheoriginalone.Ifwekepttheoldclassnames,wecouldhaveused theoldapplication.
Listing4.47RedAndGreenApplication2.mxml Whentheuserenterstheitemdescriptionandclicksthegreenone,theapplicationcreatesanew instanceoftheItemAddedEvent,passingtheentereditemtoitsconstructor,andtheShoppingCart properlydisplaystheselected”NewItemtoAdd”ontheredcarpet(seeFigure4.19).
152
RIAWITHADOBEFLEXANDJAVA
LearningFlexThroughApplications
Figure4.19TheoutputofRedAndGreenApplication.mxml
Makingcomponentslooselyboundsimplifiesdevelopmentanddistributionbutcomesatahigher costintestingandmaintenance.Dependingonthedeliverytimeline,size,andlifespanofyourapplication,you’dhavetomakeachoicebetweenlooselycoupledorstronglytypedcomponents. One last note.The itemDescription in Listing 4.45 doesn’t have an access-level qualifier. It’s socalled package-level protection.The ShoppingCart can access itemDescription directly, but the classesoutsidethe“cart”packagecan’t.
Summary ThiswasalargechapteranditjustcoveredthebasicconceptsofActionScriptprogramming.Asyou startbuildingyourFlexapplication,thecodingdescribedwillbecomeroutine.Makingachoiceof whichapproachtotakewillnot.Wehopethatsuchahigh-leveloverviewwillhelpyoutomakean informedchoiceaboutthepathtotake.
Endnotes 1. TobeexacttheappropriateJavaclassloaderoneachsideshouldbeabletofindtheclassintheparental chainoftheclassloaders. 2. Thefunctionobjectbecomesacandidateforgarbagecollection.
RIAWITHADOBEFLEXANDJAVA
153
154
RIAWITHADOBEFLEXANDJAVA
CHAPTER
5
ACompleteApplicationwith RPCCommunicationsandJMS
RIAWITHADOBEFLEXANDJAVA
155
CHAPTER5
ACompleteApplicationwith RPCCommunicationsandJMS Multi-TierApplicationDevelopmentwithFlex ThischapterdescribestheprocessofcreatingacompleteFlex-Javadistributedapplication.UpgradingFlexapplicationstoJavaEnterpriseEditionapplicationsisdonewithFlexDataServices. FDSprovidestransparentaccesstoPOJO,EJBs,andJMSandcomeswithadaptersforframeworks likeSpringandHibernate.Thesepowerfulcapabilitiescomefreeforasingle-CPUserver,otherwise seeyourlocalAdobedealer.FlexcanalsoinvokeanySOAPWebServiceorsendanHTTPrequest toanyURLviaWebServicesandHTTPServicecomponents. In this chapter we will illustrate Flex controls, HTTPService, RemoteObject, and Consumer via twoversionsofastockportfolioapplication.Thefirstversionwillshowcommunicationsbetween Flash and a plain old Java object (POJO) using Flex remoting.We’ll also explain how to use the HTTPServicetoreadtheRSSnewsfeed.Intheotherversionwe’lladdthestock(akasecurity)price feedusingFlexMessagingandtheJavaMessagingService(JMS). While explaining the FDS capabilities, we will also walk you through a typical design with Flex containers.
DesigningaStockPortfolioApplication OurgoalistocreateaWebapplicationthatwillreceiveanddisplayafeedcontainingsecuritypricesandthelatestfinancialnewsasinFigures5.1and5.2.Thissectioncontainsasortoffunctional specificationofsuchanapplication. We’llpopulatethetopportionofthescreenwiththestocksincludedintheuser’sportfolio.For simplicity’ssake,we’llstoretheuser’sportfoliointheXMLfileasinListing5.1.
MSFT 10000 33.38
156
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications 1
IBM 3000 82.15 1
…
Listing5.1Afragmentofaportfolio.xml
Figure5.1Stockportfolioscreen-theshowgridview
Whentheuserclicksonarowwithaparticularstock(i.e.,ADBEasinFigure5.1),itwillpopulate thelowerdatagridwiththeheadlinesrelatedtotheselectedstock.Thenewsshouldbecoming fromhttp://finance.yahoo.com/rss/headline.ThecolumnLinkwillcontaintheURLofthenews, RIAWITHADOBEFLEXANDJAVA
157
CHAPTER5
andwhentheuserclicksonthelink,anewbrowserwindowpopsupdisplayingtheselectednews article. ThetopofthescreencontainsthetogglebuttonsShowGrid/ShowChart.WhentheShowGridoptionisselected,theuserwillseethescreenasinFigure5.1,andwhenShowChartisselected,the topdatagridwillbereplacedwiththepiechart(seeFigure5.2),preservingthesamefunctionality (clickingonthepieslicerepopulatesthenewsheadlinesaccordingtotheselectedsecurity).The marketdataisrefreshedonthescreeneverysecondorso.
Figure5.2Stockportfolioscreen–theshowchartview
The first version of the application will query the server POJO that generates random numbers. Laterinthischapter,we’llsubscribetoJMStopicandconsumeareal-timedatafeedfromanexternalJavaapplication. Inthisapplicationwe’llusethebasicMXMLandActionScriptfromthefirstchaptersofthisbook. WeassumethatthereaderhasabasicknowledgeofJavasyntax.We’veincludedmini-references ontheJavaNamingandDirectoryInterfaceandJMS.Solet’srollupoursleeves…
158
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
AddingaDataGrid We’llstartbyaddingthetopgriddisplayingthestockportfolio.TheFlexdataGridcomponentisa naturalchoicefordatathatcanbepresentedasrowsandcolumns.MXMLisagreattoolformodularizingdevelopment.Thenameofthenew.mxmlfileautomaticallybecomesthenameofanew tagthatcanbereusedinotherfiles.Forexample,thecodeinListing5.2assumesthatthereisan MXMLfilenamedPortfolioView1.mxml(inrealityitcanalsobeanActionScriptfilenamedPortfolioView1.asoranyotherfilethatisassignedtothistagviathecomponentlibrarymanifest):
…
Listing5.3ThefirstversionofPortfolioView.mxml
RIAWITHADOBEFLEXANDJAVA
159
CHAPTER5
Evenifwewon’taddanymorecode,isn’titimpressivethatittakesonlyadozenlinesofcodeto readtheXMLfile,parseit,anddisplayitinagridshowninFigure5.3?Butthisapplicationhasa staticnature:itdoesnotconnecttoanypricequotefeed.Inotherwordsyouwouldalwayssee $33.38asthepriceforMicrosoft,and$82.15forIBM.
Figure5.3AdatagridpopulatedfromPortfolio.xml
InListing5.3,thecurlybracessurroundingtheexpressionindicatethatthisexpressionisbeing usedasasourceindatabinding.ItiscrucialforFlexprogrammingtofullyunderstandthestrengths and weaknesses of binding. Binding is based on event listeners automatically generated by the Flexcompilerasaresponsetodeclarativebindingannotations.Toinitiatebindinggeneration,developersuseacombinationofcurlybraces,mx:Bindingtags,and[Bindable]metadatadirectives. (RefertotheAdobeFlexmanualformoredetail.)InChapter4,we’vegivenyousomeexamplesof databinding,andinthischapterwe’llkeepemphasizingtheconvenienceofbindingforautomatic asynchronouscodeinvocation. Next,weneedtoperiodicallyconnecttotheserverfornewpricesandupdatethePriceandValue columns.Solet’suseaspecialFlexcomponentcalledRemoteObject: .
TheRemoteObjectcomponentallowscallingmethodsofaspecifiedremotePOJO,whichisconfiguredontheserverasadestinationPortfolioinaspecialXMLfile.We’dliketoemphasizethatFlex transparentlycallsJavafromActionScript.Theclientneedstoknowthenameofthedestination andthemethodtocall,forexample,getQuotes().Allthedirtyworkofdatamarshalingbetween ActionScriptandJavaisdoneforyoubytheFlexframework.If,forexample,aJavamethodreturns anobjectoftheStockQuoteDTO.javatype,Flexde-serializestheJavaobjectandbuildsitsActionScriptpeerontheclient.However,forperformancereasons,itisrecommendedthatyoucreatethe peerActionScriptclassandregisteritwiththeframework.We’llshowtheStockQuoteDTO.aslater inthischapter.
160
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
Please note that all RPC communications, including RemoteObject, are asynchronous. In other words,wedon’texactlycallaremotemethod,butrathersendamessagetotheserver,requesting acallofthespecificJavamethod.Notonlyistheclient’srequest(s)executedasynchronously,but evensendingtotheserverisdoneasynchronously.AndifyouneedtodomultipleRemoteObject invocationsinyourscript,Flexwillbatchthemtogetherandsendintheendofthescriptexecution. Theresultsofremoteinvocationsarereturnedviaevents.RemoteObjectprovidestheresultevent forsuccessorfaultforfailures.Youshouldwritethecorrespondinghandlerfunctions.Flexwillcall thesemethods,supplyinganEventobjectasaparameter.It’syourresponsibilitytogettheinformationfromtheeventandactaccordingly.Friendlyadvice:youwillsaveyourselfhoursoftimeif yousupplyafaulthandler. Inthenextcodesnippetwesetconcurrencytolast,becauseifFlexdecidestobatchtheoutgoing requests,wedonotwanttosendoutmorethenonerequestinabatch;ifauserclicksonthescreen sendingmorethanonerequestinquicksuccession,thelastrequestwillsuppressallpreviousones. Similarly,whentheresultsarecomingbackweareinterestedonlyintheonewesentlast:
Listing5.4RemoteObjectTag Thetagallowsusingresultandfaulthandlingonboththeobjectandmethod levels.ThemethodsettingswilltakeprecedenceovertheRemoteObject’sones. For server-side support, you have to download and install Flex Data Services 2 Express Edition (http://www.adobe.com/products/flex/),anddeployitasaWebapplicationintheJ2EEserverof yourchoice,forexample,inTomcat.FDScomeswithasetofXMLfiles,whichyouwillusetoconfigureyourserver-sideobjects. ToregisteraPOJOclasswithaFlexclientweneedtoupdatetheconfigurationfileontheserver side.Thisletsyouhidedetailsoftheserviceprovider(i.e.,actualJavaclassnames)byspecifyingsocalleddestinationswhereyouspecifyaccessconstraints,etc.Thefollowingsectionhastobeadded totheremoting-config.xmlfile.
com.theriabook.ro.Portfolio
Listing5.5ConfiguringaFlexremotingservice
RIAWITHADOBEFLEXANDJAVA
161
CHAPTER5
Clientswon’tknowthattheactualnameofourPOJOiscom.theriabook.ro.Portfolio,butthey’llbe abletorefertoitbythenicknamePortfolio.Flexlooksforclassesspecifiedindestinationmappings ontheWebApplicationclasspathincludingjarsinsideWEB-INF/libandclassesunderWEB-INF/ classes.TheJavaclassPortfolio.java(seeListing5.6)isasimplerandomnumbergeneratorsimulatingmarket-likereal-timepricechangesforseveralhard-codedsecurities. package com.theriabook.ro; import java.util.Random; import com.theriabook.jms.dto.StockQuoteDTO;
}
public class Portfolio { static Random random = new Random(); static StockQuoteDTO[] quotes = { new StockQuoteDTO(“IBM”, 82.0), new StockQuoteDTO(“MSFT”, 27.0), new StockQuoteDTO(“ADBE”, 38.0), new StockQuoteDTO(“ORCL”, 13.0)}; double volatility=.05; public StockQuoteDTO[] getQuotes() { for (int i = 0; i < quotes.length;i++){ quotes[i].last += random.nextGaussian()* volatility; } return quotes; }
Listing5.6Asimplestockquotegenerator–Portfolio.java TheStockQuoteDTO.Java(seeListing5.7)containsthelastpriceofaparticularstock. package com.theriabook.jms.dto; import java.io.Serializable; public class StockQuoteDTO implements Serializable { private static final long serialVersionUID = 4672447577075475117L; public String symbol; public double last; public StockQuoteDTO(String sym, double newPrice){ symbol = sym; last = newPrice; } }
Listing5.7TheJavaDTO-StockQuoteDTO.java
162
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
However, the client can really benefit from knowledge of the structure and the datatypes of the returnedDTOs.Listing5.8showstheActionScript’scounterpartfortheStockQuoteDTO.javaobject.WhileFlexdoesnotneedthisdefinitioninordertodeserializetheJavaobjectthatincludes membervariablesofstandardtypes(bydefaultitcreatesadynamicobjectandaddstherequired propertiesoftheJavaobjectthat’sbeingdeserialized),itdoeshelpperformance(sincethedeserializedobjectisimmediatelyallocatedinmemory),ensurestheoutputdatatypesandenforcesthe typeconversion.The[RemoteClass…]metadatatagabovetheclassdefinitiontellstheFlexde-serializationmethodtousethisparticularclassforde-serializationwhenevertheserversendscom. theriabook.jms.dto.StockQuoteDTOobjectdown. package com.theriabook.jms.dto { [RemoteClass(alias=”com.theriabook.jms.dto.StockQuoteDTO”)] public dynamic class StockQuoteDTO { public var symbol:String; public var last:Number; }
Listing5.8TheActionScriptontheclient:StockQuoteDTO.as Whentheclientsuccessfullygetsthequotes,itprocessesthemandasksfornewones: private function onResult(event:ResultEvent):void { … var quotes:Array = event.result as Array; applyQuotes(quotes); // Pull the next set of quotes pullQuotes(); } private function applyQuotes(quotes: Array):void { for (var i:int=0; i
Listing5.13PortfolioView2.mxml Forthepiechart,we’veselectedacallouttypeoflabelpositioning,specifiedsothesizeofthepie isproportionaltotheattributeValue,andwe’veaddeda3Ddepthtothechartbysettingexplode
170
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
Radiusto0.02.Notehowwe’veassignedthefunctionnameshowPositiontothelabelFunctionattributeofthepiechart.ThesignatureofthelabelFunctionassumesthatthefirstargumentbrings theelementofthedataProvidercorrespondingtothecurrentwedgeintheseries.
Chart/DataGridToggling Imagineadeckofplayingcards:onlythetopcardisvisible.Hidethetopcardandyou’llseethenext one.We’llusethedeckoftwo“cards”:onecardwilldisplaythegrid,andanotherone–thechart.ToJava SwingdevelopersthisshouldlookliketheCardLayout.InFlexjargonit’scalled.The screensnapshotinFigure5.6wasmadewhentheportfolioPiewasonthetopoftheportfolioGrid:
Figure5.6UsingViewStackfortoggling
AfragmentofPortfolioView3.mxmlintroducesMXMLcomponents,,and.
172
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
Listing5.15PortfolioView3.mxml
174
RIAWITHADOBEFLEXANDJAVA
ACompleteApplicationwithRPCCommunications
ThetogglebuttonsShowGrid/ShowChartuseimages,andit’sagoodideatoembedtheseresourcesrightintotheSWFfile.ThiswaythebrowsercandownloadtheentireclientinoneHTTPrequest (butthesizeoftheSWFfilebecomeslarger).Forthesamereason,themulti-fileJavaappletsare packagedintoasinglejar. Thenextstepistomovethetogglebuttonsup:
Figure5.7UsingViewStackfortoggling
Inthepreviouscodesample,weusecanvasestowrap,butnowwe’regoingwrapuptheentirePortfolioPanel.Yes,thePanelwillbecomeachildoftheCanvas.Hereiswhyweneedit.TheCanvasis thesimplestcontainer,andit’salsotheabsolutepositioningcontainer.Inotherwords,ifyouplace AandBaschildrenoftheCanvas,theyoverlapunlesseachofthemhasspecificxandycoordinate settings.Sowe’replanningonoverlappingtheToggleButtonBarandthePortfolioPanelinasingle Canvas.Asanadditionalmeasure,tomaketheToggleButtonBarappearonthefarright,weput theToggleButtonBarinsidetheHBoxcontainer.MakesurethattheRemoteObjectandtheXML aremovedouttobecomechildrenoftheCanvas;theyhavetobeattheoutermostlevel–thisisan MXMLrequirement.
RIAWITHADOBEFLEXANDJAVA
181
CHAPTER5
OurnewsFeedwillinitiatethefollowingrequestfromthecreationCompleteevent: newsFeed.send({s:”ADBE”});
Aspartofthesendmethod,Flexconvertstheobject’spropertyintoaGETURLparameterusing the object’s properties as parameter names. Here s:”ADBE” will be concatenated in the form of “?s=ADBE”tothehttp://finance.yahoo.com/rss/headlinespecifiedbythenewsdestination.Since RSSdatacanbeverbose,we’llsettherelevantcolumnsofthegridwithwordWrap=”true”andthe heightoftheGridrowtobeflexible:variableRowHeight=”true”.
Listing5.19Anapplicationfilenews2.mxml TheFinancialNews2tagisspelledoutinListing5.20.
Listing7.2DestinationAwareCollectionDemoapplication
Figure7.1AsnapshotofDestinationAwareDemooutput
This application contains a data grid with a dataProvider bound to an instance of DestinationAwareCollection.ThereisnoRemoteObjectinsight,noeventhandlersforResultEventandFaultEvent.The only data-related code contained in our application is the initialization of collection parametersandtheinvocationofitsfill()method: collection = new DestinationAwareCollection(); collection.destination = “Employee”; collection.method = “getEmployees”; collection.fill(new Date());
Simple,isn’tit?Butwhat’sinsidethisblackboxcalledDestinationAwareCollection?
MakingaDestination-AwareCollection ThesimplicityofDestinationAwareCollectiontakesitsrootsinthewayit’sconstructed.Tomake DestinationAwareCollection, Listing 7.3, we extend an ArrayCollection and add two public variables:destinationandmethod: public class DestinationAwareCollection extends public var destination:String=null;
ArrayCollection {
RIAWITHADOBEFLEXANDJAVA
255
CHAPTER7
}
public var method : String = null;
Thenweintroduceapublicmethodfill().SinceallAMFinvocationsareasynchronousmessages,we designfill()toreturnanAsyncToken,whichisalocallycachedobject.Inyourtypicalscenario,AsyncTokengetsaccessedtwice:youuseittostoresomedatarelatedtotheparticularrequestduringthe sendandthen,whentheresponsecomes,youlookupthesamedataforajust-in-timeperusal: public function fill(... args): AsyncToken { var act:AsyncToken = invoke(method, args); act.method = “fill”; return act; }
Nowlet’sgettotheimplementationdetails.Theonlyparameterofthefill()methodisdefinedas… restarray,whichinturnispassedtotheinvocationoftheremotingoperation: protected function invoke(method:String, args:Array):AsyncToken { if( ro==null ) ro = createRemoteObject(); ro.showBusyCursor = true; var operation:AbstractOperation = ro.getOperation(method); operation.arguments = args; var act:AsyncToken = operation.send(); return act; }
The createRemoteObject() function ties a dynamically created RemoteObject with its_onResult() andonFault()methods.Pleasenoticethatweenforced“last”asro’sconcurrencysetting,whichwill resultincancelingpendingoutgoingrequests,ifany2: protected function createRemoteObject():RemoteObject { var ro:RemoteObject; if( destination==null || destination.length==0 ) throw new Error(“No destination specified”);
}
ro = new RemoteObject(); ro.destination = destination; ro.concurrency = “last”; ro.addEventListener(ResultEvent.RESULT, ro_onResult); ro.addEventListener(FaultEvent.FAULT, ro_onFault); return ro;
In the case of FaultEvent, we just pop up an optional Alert. But in the ResultEvent handler we
256
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
changetheunderlyingdatasourceofthecollection,andthat’swhywecalltheArrayCollection’s refresh()methodupdatingalltheviewsassociatedwiththecollection: protected function ro_onResult(evt:ResultEvent):void { CursorManager.removeBusyCursor(); dispatchEvent(evt); source = evt.result.source; refresh(); }
Inbotheventhandlerswere-dispatchtheeventtothecollectionitself.Youmaywanttoaddmore logichere,allowingyourcollectiontopreventthedefaultprocessing.Forinstance,youmayhave somenewcontentappendedtothecollectioninsteadofwipingouttheexistingrows.ThefulllistingofDestinationAwareCollectionispresentedinListing7.3: // DestinationAwareCollection.as package { import import import import import import import import
mx.collections.ArrayCollection; mx.controls.Alert; mx.managers.CursorManager; mx.rpc.AbstractOperation; mx.rpc.AsyncToken; mx.rpc.events.FaultEvent; mx.rpc.events.ResultEvent; mx.rpc.remoting.mxml.RemoteObject;
[Event(name=”result”, type=”mx.rpc.events.ResultEvent”)] [Event(name=”fault”, type=”mx.rpc.events.FaultEvent”)] public class DestinationAwareCollection extends ArrayCollection { public var destination:String=null; public var method : String = null; public var alertOnFault:Boolean=true; protected var ro:RemoteObject = null; public function DestinationAwareCollection(source:Array=null){ super(source); } public function fill(... args): AsyncToken { var act:AsyncToken = invoke(method, args); act.method = “fill”; return act; } RIAWITHADOBEFLEXANDJAVA
257
CHAPTER7 protected function invoke(method:String, args:Array):AsyncToken { if( ro==null ) ro = createRemoteObject(); ro.showBusyCursor = true; var operation:AbstractOperation = ro.getOperation(method); operation.arguments = args; var act:AsyncToken = operation.send(); return act; } protected function createRemoteObject():RemoteObject { var ro:RemoteObject; if( destination==null || destination.length==0 ) throw new Error(“No destination specified”);
}
}
}
ro = new RemoteObject(); ro.destination = destination; ro.concurrency = “last”; ro.addEventListener(ResultEvent.RESULT, ro_onResult); ro.addEventListener(FaultEvent.FAULT, ro_onFault); return ro;
protected function ro_onResult(evt:ResultEvent):void { CursorManager.removeBusyCursor(); dispatchEvent(evt); source = evt.result.source; refresh(); trace(“RESULT: “ + destination + “::” + method + “() returned “ + evt.result.source.length + “ records”); } protected function ro_onFault(evt:FaultEvent):void { CursorManager.removeBusyCursor(); dispatchEvent(evt); if (alertOnFault) { Alert.show(evt.fault.faultString + evt.fault.faultDetail, “Error calling destination “ + evt.message.destination); } }
Listing7.3DestinationAwareCollection.as
258
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
SensingCollectionChanges WhenevertheunderlyingdataofanArrayCollectionchanges,itdispatchesaCollectionEventwith an event.kind property containing the values“add,”“remove,”“update,”“reset,” etc. By tapping intothemechanismoftheCollectionEvent,wearegoingtoimproveourdestination-awarecollection.Ultimatelywewillbemaintainingacloneoftheoriginalrecordforeachrecordthat’sbeen modifiedaswellaskeepinganexactrollofallnewanddeletedrecords.First,though,let’sillustrate theCollectionEvent.SupposeweupgradeourtestingapplicationbyaddingthetwobuttonsRemoveandAddasshowninFigure7.2:
Figure7.2SnapshotofChangeSensitiveCollectionDemo
ThecodeofthenewChangeSensitiveCollectionDemotestingapplicationispresentedinListing 7.4.We’ve made the data grid editable and changed the collection references from DestinationAwareCollectiontoChangeSensitiveCollection:
RIAWITHADOBEFLEXANDJAVA
275
CHAPTER7
’}”/>
’}”/>
’}”/>
0 || deletedCount>0); }
RIAWITHADOBEFLEXANDJAVA
279
CHAPTER7
}
public override function set modifiedCount(val:uint ) : void{ super.modifiedCount = val; commitRequired = (modifiedCount>0 || deletedCount>0); } . . . .
ThemostnotablefeatureoftheUpdatableCollectionisitssync()method.Itsendsanarrayof changestotheserver.Ourimplementationofsync()willassumesuccessandsowe’llclearthe stateinformationinadvance,rightontheasynchronoussend(invoke).However,tohandleany potentiallyunpleasantsurprises,we’llpreservethesamestateinformationintheasynchronous token: public function sync(): void { var act:AsyncToken = invoke(method + “_sync”, [new ArrayCollection(changes)]); act.method = “sync”; act.modified = modified; act.deleted = deleted; act.modifiedCount=modifiedCount; resetState(); } public function get changes():Array { var args:Array = []; for ( var i :int = 0; i < deleted.length; i++) { args.push( new ChangeObject( ChangeObject.DELETE, null, deleted[i] ) ); } for ( var obj:Object in modified) { args.push( modified[obj]); } return args; }
Now,ifthingsdon’tworkout,we’llrestorethestatusquointheFAULTeventhandler: override protected function ro_onFault(evt:FaultEvent):void { CursorManager.removeBusyCursor(); if (evt.token.method == “sync”) { modified = evt.token.modified;
280
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices modifiedCount = evt.token.modifiedCount; deleted = evt.token.deleted;
}
} dispatchEvent(evt); if (alertOnFault) { Alert.show(evt.fault.faultString, “Error on destination: “ + evt.message.destination); }
Weareallset,shortofseparatingaResultEventrelatedtosync()fromaResultEventoriginatingin responsetofill(): override protected function ro_onResult(evt:ResultEvent):void { CursorManager.removeBusyCursor(); if (evt.token.method == “fill”) { source = evt.result.source; trace(destination + “::” + method + “() fill returned “ + evt.result.source.length + “ records”); resetState(); refresh(); } else { trace( destination + “::” + method + “() completed”); dispatchEvent(evt); }
}
Believeitornot,butthisisallthereistotheUpdatableCollection;thecompletecodeisinListing 7.11: // UpdatableCollection.as package { import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.events.*; import mx.managers.CursorManager; import mx.rpc.AsyncToken; import mx.rpc.events.*; import ChangeObject; public class UpdatableCollection extends
ChangeTrackingCollection {
// We need this to allow marshalling of DataSyncException into // DataErrorMessage as part of FaultEvent RIAWITHADOBEFLEXANDJAVA
281
CHAPTER7 // Otherwise we will get something like // type Coercion failed: cannot convert ... to mx.messaging.messages.IMessage import mx.data.messages.DataErrorMessage; private var linkage:DataErrorMessage; public function UpdatableCollection(source:Array=null){ super(source); } private var _commitRequired:Boolean = false; public function set commitRequired(val :Boolean) :void { if (val!==_commitRequired) { _commitRequired = val; dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, “commitRequired”, !_commitRequired, _commitRequired)); } } public function get commitRequired() :Boolean { return _commitRequired; } public override function set deletedCount(val:uint):void { super.deletedCount = val; commitRequired = (modifiedCount>0 || deletedCount>0); } public override function set modifiedCount(val:uint ) : void{ super.modifiedCount = val; commitRequired = (modifiedCount>0 || deletedCount>0); } public function get changes():Array { var args:Array = []; for ( var i :int = 0; i < deleted.length; i++) { args.push( new ChangeObject( ChangeObject.DELETE, null, deleted[i] ) ); } for ( var obj:Object in modified) { args.push( modified[obj]); } return args; }
282
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
public function sync(): void { var act:AsyncToken = invoke(method + “_sync”, [new ArrayCollection(changes)]);
}
act.method = “sync”; act.modified = modified; act.deleted = deleted; act.modifiedCount=modifiedCount; resetState();
override protected function ro_onFault(evt:FaultEvent):void { CursorManager.removeBusyCursor(); if (evt.token.method == “sync”) { modified = evt.token.modified; modifiedCount = evt.token.modifiedCount; deleted = evt.token.deleted; } dispatchEvent(evt); if (alertOnFault) { Alert.show(evt.fault.faultString, “Error on destination: “ + evt.message.destination); } } override protected function ro_onResult(evt:ResultEvent):void { CursorManager.removeBusyCursor(); if (evt.token.method == “fill”) { source = evt.result.source; trace(destination + “::” + method + “() fill returned “ + evt.result.source.length + “ records”); resetState(); refresh(); } else { trace( destination + “::” + method + “() completed”);
} }
} dispatchEvent(evt);
}
Listing7.11UpdatableCollection.as
RIAWITHADOBEFLEXANDJAVA
283
CHAPTER7
Now let’s spin our UpdatableCollection. The testing application, UpdatableCollectionDemo, ispracticallythesameastheoneweusedtotestChangeTrackingCollection;we’vejustaddeda “Commit”buttonandchangedthecollectionreferences:
RIAWITHADOBEFLEXANDJAVA
291
CHAPTER7
. . . .
com.theriabook.remoting.BatchGateway
Listing7.16Remoting-config.xmlwith“batchGateway”destination
BatchMember:OrderMatters WehavecreatedourownBatchGatewaysolet’sseehowtouseit.TocallexecuteTransactionBatch(), weneedtoprepareanarrayofActionScriptclassesthatwillbemarshaledintotheJavaListcom. theriabook.remoting.BatchMemberobjectsexpectedbyBatchGateway.Here’sthedefinitionofthe matchingActionScriptclass: package com.theriabook.remoting { [RemoteClass(alias=”com.theriabook.remoting.BatchMember”)] public class BatchMember { public var destinationName:String; public var methodName:String; public var parameters:Array; public function BatchMember(destinationName:String, methodName:String, parameters:Array=null) { this.destinationName = destinationName; this.methodName = methodName; this.parameters = parameters; }
292
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
}
}
public function toString():String { return “com.theriabook.remoting.BatchMember { “+” destinationName:”+destinationName+” methodName:”+methodName+” parameters:”+parameters + “}” };
Listing7.17ActionScriptBatchMemberclass Wearealmostthere:foreachcollectionthatneedstojointothebatch,wecanwritesomething like: var bm:BatchMember; var batch:Array=[]; bm = new BatchMember( orderCollection.destination, orderCollection.syncMethod, [orderCollection.changes] ); batch.push(bm);
Then we could place a remote call to BatchGateway and process the callbacks. Mission accomplished…orsoitseems. Here’stheproblem,however.Thesimple_orderandsimple_order_itemdatabasetablesrepresent a master/detail relationship. In referential integrity terms, simple_order_item could have had a foreignkeyonthecolumnorder_idfromsimple_order.Inthisscenarioyouwon’tbeabletoinsert anitemwithanon-existentforeignkeyvalue;yourtransactionwouldsucceedonlyifyouinserted ordersfirst. Inthecaseofadeleteoperation,aforeignkeydefinitioncouldpossiblyprescribeacascade-deletionofalldependentorderitems,whenanorderisbeingdeleted.That’ssomethingwecouldlive with. But how about this: a foreign key could restrict the deletion of the order until the dependantorderitemsexist.Inotherwords,trytosyncyourcollectionsinthewrongorderandyou’re doomed!
BatchService Tosolvetheproblemidentifiedabove,we’llcreateaBatchServiceclassthatoncegivenacollection, addsthreeBatchMemberstothebatch:onewithdeletes,onewithinserts,andonewithupdates.In thecaseofmorethanonecollection,ourBatchServicewillconsideritsprioritiesandadddeletesforall thecollectionsinreverseorderofpriority,thenaddinsertsandupdatesforall–intheoppositeorder. EarlierwepromisedtoexplainwhyontheJavaDAOsidewe’vesplitthejobofsync()intothreecalls ofthissequence: RIAWITHADOBEFLEXANDJAVA
293
CHAPTER7 getOrders_deleteItems(items); getOrders_updateItems(items); getOrders_insertItems(items);
Thereasonistheneedtoexposethesemethodsforremoting.AsAntonChekhovusedtosay6:“Ifin thefirstactyou’vehungapistolonthewall,theninthefollowingoneitshouldbefired.” We’llseehowpistolsfirefrominsideourBatchServiceafterintroducingaBatchService’sAPI.Supposewehavetwocollections–ordersandorderItems: orders = new DataCollection(); orders.destination=”Order”; orders.method=”getOrders”; orderItems = new DataCollection(); orderItems.destination=”Order”; orderItems.method=”getOrderItems”;
Oncethecollectionsarecreated,weregisterthemwiththeBatchServicespecifyingtheirpriorities: batchService = new BatchService(); batchService.addEventListener(FaultEvent.FAULT, onCommitFault); batchService.registerCollection(orders,0); batchService.registerCollection(orderItems,1);
Whenit’stimetosynchronize–perhapswhentheuserclicksontheSavebutton–weasktheBatchServicetomakethebatchandsendittotheBatchGateway: var batch:Array = batchService.batchRegisteredCollections(); batchService.sendBatch(batch);
That’sallthereistoaBatchServiceAPIandwecanturnourattentiontotheimplementation,see Listing7.18.ThefocalpointhereisthebatchRegisteredCollections()method.Thismethodsends accumulated collection changes three times,7 which, in case of a getOrdersDAO, translates to a sequentialcallviatheBatchGateway: getOrders_deleteItems(items) getOrders_updateItems(items) getOrders_insertItems(items)
Theothertwopublicmethods–registerCollection()andsendBatch()–arerathersimple.Please notehowwere-dispatchResultEventandFaultEventfromtheremoteobjecttotheinstanceofthe BatchServicesothattheapplicationcanprocessitaccordingly.
294
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices package com.theriabook.remoting { import com.theriabook.collections.DataCollection; import com.theriabook.remoting.BatchMember; import mx.rpc.events.*; import mx.managers.CursorManager; import mx.rpc.*; import mx.rpc.remoting.mxml.RemoteObject; import flash.events.EventDispatcher; [Event(name=”result”, type=”mx.rpc.events.ResultEvent”)] [Event(name=”fault”, type=”mx.rpc.events.FaultEvent”)] public class BatchService extends EventDispatcher { public static const BATCH_GATEWAY_DESTINATION:String=”batchGateway”; protected var ro:RemoteObject = null; private var registeredCollections:Array=[]; {
public function registerCollection(collection:DataCollection, priority:int=0):void }
registeredCollections.push({collection:collection, priority:priority});
public function batchRegisteredCollections():Array { var batch:Array = []; registeredCollections.sort(sortOnPriority); var collection:DataCollection; var member:BatchMember; for (var i:int=registeredCollections.length-1; i>=0; i-- ) { collection = registeredCollections[i].collection as DataCollection; member = new BatchMember( collection.destination, collection.method + “_deleteItems”, [collection.changes] ); batch.push(member); } for (i=0; i< registeredCollections.length; i++ ) { collection = registeredCollections[i].collection as DataCollection; member = new BatchMember( collection.destination, collection.method + “_updateItems”, RIAWITHADOBEFLEXANDJAVA
295
CHAPTER7 [collection.changes] ); batch.push(member);
}
} for (i=0; i< registeredCollections.length; i++ ) { collection = registeredCollections[i].collection as DataCollection; member = new BatchMember( collection.destination, collection.method + “_insertItems”, [collection.changes] ); batch.push(member); } return batch;
public function sendBatch(batch:Array, transaction:Boolean=true): AsyncToken { if( ro==null ) ro = createRemoteObject(); var operation:AbstractOperation = ro.getOperation(transaction?”executeTransactionBatch”:”executeBatch”); operation.arguments = [batch]; var act:AsyncToken = operation.send(); act.arguments = batch; return act; } private function createRemoteObject():RemoteObject { var ro:RemoteObject = new RemoteObject(BATCH_GATEWAY_DESTINATION); ro.showBusyCursor = true; ro.concurrency = “last”; ro.addEventListener(ResultEvent.RESULT, ro_onResult); ro.addEventListener(FaultEvent.FAULT, ro_onFault); return ro; } private function ro_onFault(event:FaultEvent):void { CursorManager.removeBusyCursor(); dispatchEvent(event); } private function ro_onResult(evt:ResultEvent):void { var collection:DataCollection; for (var i:int=0; i< registeredCollections.length; i++ ) { collection = registeredCollections[i].collection as DataCollection;
296
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
}
}
}
collection.resetState(); } CursorManager.removeBusyCursor(); dispatchEvent(evt);
private function sortOnPriority(a:Object, b:Object):int { var pa:int = a.priority; var pb:int = b.priority; if (pa > pb) { return 1; } else if (pa < pb) { return -1; } else { //pa == pb return 0; } }
Listing7.18BatchService.as
ASampleApplication–AnOrderEntry The following sections describe a sample application – OrderEntryDemo – that allows transactionaldataentryintothetablessimple_orderandsimple_order_item.Theapplicationiscomposed oftwopanels:OrdersPanelandOrderItemsPanelwithcontrollerclass–OrderManagerasshown inListing7.19:
Listing7.23OrdersPanel.mxml
OrderEntryDemo:OrderItemsPanel The bottom panel of theapplication, OrderItemsPanel,populatesitsdatagridwithdatafroma bindableorderItemscollection,hostedbytheOrderManager:
[Bindable]private var collection:DataCollection ; [Bindable]private var orderManager:OrderManager ; private function onCreationComplete() : void {
302
RIAWITHADOBEFLEXANDJAVA
HowtoWriteYourOwnDataManagementServices
}
orderManager = Application.application.orderManager; collection = orderManager.orderItems;
Similar to the OrdersPanel, the buttons Add and Remove delegate their work to the OrderManager: private function onAdd(position:int):void { orderManager.addOrderItem(position); dg.selectedIndex = position; } private function onRemove(position:int):void { orderManager.removeOrderItem(position); }
ThecompletecodeoftheOrderItemsPanelispresentedinListing7.24:
366
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation
Listing9.1PlainXmlTreeDemo.mxml TheTreecontrolgetsitsdatafromahierarchicaldataprovider.SincethedataProviderpropertyof thecontrolisimplementedasasetterfunction,there’salotofflexibilityinwhatwecanassign.In particular,insteadoftheE4XXMLobject(above)wecouldhaveassignedastringwithavalidXML text, XMLList, XMLListCollection, or any object that could be interpreted as a hierarchical data provideraccordingtotherulesoftheITreeDataDescriptorinterface. In Listing 9.1, only the Pennsylvania node has a real child – the of Philadelphia. ButifyoulookatFigure9.1,you’llnoticethatCaliforniaandArizonaarealsofoldernodes.We’ve achieved this effect by using the node attribute isBranch=”true.” Similarly, if for any reason you don’twanttoprocessaparticularbranchofthetree,explicitlysetisBranch=“false”andthechild nodeswillbeignored. EachnodeinourXMLcontainsanattribute“label”thatissupposedtobedisplayedasavalueof thetreenode.Haditbeenanarrayofobjects,eachcontainingalabelproperty,Treecontrolscould readily act on that. However, we’re dealing with XML and because of that we have to explicitly specifyviathelabelFieldpropertyoftheTreecontrolthatourlabelsarestoredasattributes: labelField=”@label”
Why?Theattribute“label”andthechildnodeareequallygoodchoices(andthat’sjustfor startersbecausewecouldhavehadachildnodewiththeattribute“label”,etc.).Howon earthdoestheTreecontrolknowthelocationofthelabel?Itdoesn’t,untilweintervene. AnotherspecificofXMLdatasourcesisthattheyalwayshavearootnode.Ifyoudon’twanttodisplaytherootnode,justsettheshowRootpropertytofalse.
RIAWITHADOBEFLEXANDJAVA
367
CHAPTER9
Figure9.1AplainXML-basedTreeViewcomponent
Our next example will use an Array as a data provider. Note that we don’t need to set a showRoot=”false”here.Wealsodon’thavetoexplicitlydeclarealabelField=“label”becausethe labelattributeisassumedbydefault. Inthenextexamplewe’llusenestedarraysofActionScriptobjectstopopulatethetree.Adefault implementationofITreeDataDescriptorinterface–DefaultDataDescriptor–interpretsthechildren propertyasarepositoryofthebranchnodes.WithMXMLwecanlayoutsub-nodearrayswithan mx:childrentag:
Listing9.3CompositeAsyncLocalDataTreeDemo.mxml During the application start, the CompositeAsyncLocalDataTreeDemo registers a listener to the tree’sitemOpeneventandthenemulatestheRPCcalltopopulatethechildrenoftherootnode: public function onCreationComplete():void { tree.addEventListener(“itemOpen”, tree_onItemOpen); //Emulate asynch call to RPC object CursorManager.setBusyCursor(); var RESPONSE_TIME:uint = 2000; setTimeout(onRootChildrenReady, RESPONSE_TIME, bigStates); }
The code snippet above says,“Two seconds from now please call the function onRootChildrenReady(),passingittheargumentarraybigStates.” OurfunctiononRootChildrenReady()simplyassignsthearrayofthestatestothetree’sdataProvider.Sincewe’vestartedthebusycursorduringtheonCreationComplete(),onthedata“return” weneedtotakeitout: public function onRootChildrenReady(children:Array):void // Imagine that the data came back from RPC tree.dataProvider = new ArrayCollection(children); CursorManager.removeBusyCursor(); }
Nowthatwe’vetakencareoftherootlevel,let’slookatthechildren.Theexpansionofatreeitem
372
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation
willbeinterceptedbyoureventlistener–tree_OnItemOpenshownbelow.Pleasenotethat“item” isaninstanceoftheObject,i.e.,thedynamicclass.Thepropertyitem.isPopulatedisadynamic propertythat’ssetjustoncetoindicatethatthenodehasbeenpopulated. Priortosendingarequestforchildrenwecreateavisualnodethatcontainsthelabel“…”(ellipsisItem)toindicatetheprogresstotheuser.Itwillberemovedwhenthedatacomesbackandthe folderisfilled. public function tree_onItemOpen(event:TreeEvent):void { var item:Object = event.item; if (item.isPopulated == undefined) { item.isPopulated = true; CursorManager.setBusyCursor(); tree.dataDescriptor.addChildAt( item, {label:”...”}, 0, tree.dataProvider ); //Emulate asynch call to RPC object var RESPONSE_TIME:uint = 2000; setTimeout(onChildrenReady, RESPONSE_TIME, item, stateCities[item.data]); } }
Assoonasthefirstchildarrives,wehavetoremovetheellipsisandrepopulatethebranchbyaddingtherealchildren.WecastthechildrenoftheitemtoIList,whichgivesusanagnosticwayof navigatingthroughthecollection,beitacollectionbasedonXMLListorArray.OnceellipsisItem isremoved,weaddallchildreniteratively.Inbothitemremovalandaddition,wedelegatetothe tree’sdataDescriptor.Toensuretheimmediatevisualupdateofthetreebranch,wecollapseand thenexpandtheitemagain: public function onChildrenReady(item:Object, children:Array):void { var list:IList = IList(tree.dataDescriptor.getChildren(item,tree.dataProvider)) var ellipsisItem:Object = list.getItemAt(0); tree.dataDescriptor.removeChildAt( item, ellipsisItem, 0, tree.dataProvider ); for (var i:int=0; i
Listing9.8MXMLapplicationwiththedestination-awareTree ThiscodelooksmoreelegantthantheoneinListing9.7,doesn’tit?Thisisencapsulationinaction. Inparticular,wewillmovethecreationComplete()codefromtheapplicationinListing9.7intothe customTreecontrol’screationComplete().This,inturn,willenableustokeeptheonItemOpen() eventlistenerasaprivatemethodofourTree. Also,similartotheDataCollectionanddestination-awareComboBox,wewillcreateafill()method, whichexecutestheremotecallagainsttheexternallyprovideddestination.Tosimplifythecode, westicktothenamegetChildren().(Incomparison,inChapter8weshowedhowtoparameterize bothdestinationandmethod.) Naturally, we will localize the instantiation of the RemoteObject along with onResult() and onFault()callbacks.Notice,however,thatnowwewillchangetheconcurrencymodetomultiple,so thatthefastclickingusercouldexpandmorethanoneitematatime.Thelastremainingmoveis toencapsulateonChildrenReady()andthatwillcompleteourjob:
Listing9.10CheckedTreeDemo.mxml This code is pretty compact for the obvious reason: all functionality is encapsulated in the CheckedTreeItemRenderer.as.Nowwe’llstartbuildingthisitemrenderer.
CustomizingtheTreeItemRenderer Therearetwotypesofitemrenderers: •
drop-inrendererisaclassimplementingseveralmandatoryinterfaces,IDropInListItemRendererinparticular.Anexplicitclassofthedrop-inrenderercanbespecifiedasthevalueofthe itemRendererpropertyofalist-derivedcontrol(youcanspecifyaclassnameoranexisting instanceoftheclass).Creatingadrop-inrenderermaytakeabitmoretime,butitwillgive youareusablecomponent
•
Inlineisarenderimplementedusingandtagsto definetherenderercomponentinsidetheMXMLofthehostingListcontrol.Inlinerenderers aregoodforadhoc/prototypingpurposes,buttheycluttercode,especiallyifyouhavemore thenoneinthesameMXML.Also,onceyouneedacorrectioninyourrenderer,you’dhaveto replicatethechangeinallinstances.
TheFlexTreecontrolemploysadefaultdrop-inrenderercalledTreeItemRenderer.Thisrenderer drawsthetextassociatedwitheachiteminthetree,anoptionalicon,and–forbranchnodes–a defaulttriangledisclosureicon(bydefault,thetriangle)thattheusercanclickontoexpandorcollapsethebranch. Ifyoupeekatthesourcecodeofthisrenderer–mx.controls.treeClasses.TreeItemRenderer.as–you’ll seeallinterfacesthatitimplements: public class TreeItemRenderer extends UIComponent implements IDataRenderer, IDropInListItemRenderer, IlistItemRenderer { public var disclosure:IFlexDisplayObject; public var icon:IFlexDisplayObject; RIAWITHADOBEFLEXANDJAVA
387
CHAPTER9
}
public var label:UITextField; …
Inourcase,whenwe’readdingacheckboxtoeachitemoftheTree,extendingTreeItemRenderer seemsquitenatural.Accordingly,we’llbebuildingourcustomCheckedTreeItemRendererasadescendantofTreeItemRenderer. AccordingtotheFlexmanual,allchildrenofthecustomcomponentsmustbecreatedinthecreateChildren()method.TreeItemRendererfollowstherulesandsowillwe.Inourfirstversionofthe customrenderer–CheckedTreeItemRenderer1–we’lloverridethecreateChildren()method.That willletuscreateadefaultsetofchildrenbydelegatingtosuper.createChildren()andthenaddan extraCheckBoxchild.PleasenotehowwefollowtheFlexcomponentcreationpracticeofpassing thecomponent’sstyledefinitiontoitschildren: package com.theriabook.controls { import mx.controls.CheckBox; import mx.controls.treeClasses.*; public class CheckedTreeItemRenderer1 extends TreeItemRenderer { public var checkBox:CheckBox; override protected function createChildren():void { super.createChildren(); if (!checkBox) { checkBox = new CheckBox(); checkBox.styleName = this; addChild(checkBox); } } } }
Listing9.11ThefirstversionoftheCheckedTreeItemRenderer ThisfirstversionofthecustomrendererproducesthescreenshowninFigure9.6.Wehaveomitted thelistingoftheCheckedTreeDemo1applicationsinceitistheexactreplicaofCheckedTreeDemo withCheckedTreeItemRenderer1insteadofCheckedTreeItemRenderer:
388
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation
Figure9.4Thefirstversionofthecheckboxedtree
Theemptysquaresontheleftsidearetheemptycheckboxesaddedbytherenderer.Clearlythe checkboxesaren’tparticipatinginthelayoutandhavelandedattheleftmostpositionofeachList item. Followingthemanual,positioning(aswellassizing)thecomponentchildrenhastobedonevia theupdateDisplayList()method,whichiscalledbeforetheFlexcomponentappearsonthescreen. FlexschedulesacalltotheupdateDisplayList()inresponsetooneormorecallstoanothermethod –invalidateDisplayList().Atanyrate,theveryexecutionoftheupdateDisplayList()happensduring thenextrenderingevent.TheimplementationoftheaddChild()methodalsoautomaticallyresults ininvalidateDisplayList()andsocausesupdateDisplayList(). Inotherwords,wecanaddresspositioningissuesbyoverridingupdateDisplayList(),whichisguaranteedtohappenduetoouraddChild()inparticular.Thesecondversionoftherendererisshown inListing9.12. //CheckedTreeItemRenderer2.as package com.theriabook.controls { import mx.controls.treeClasses.*; import mx.controls.*; public class CheckedTreeItemRenderer2 extends TreeItemRenderer { public var checkBox:CheckBox;
RIAWITHADOBEFLEXANDJAVA
389
CHAPTER9 override protected function createChildren():void { super.createChildren(); if (!checkBox) { checkBox = new CheckBox(); checkBox.styleName = this; addChild(checkBox); } } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if (checkBox) { checkBox.x = (icon)?icon.x:label.x; checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight); if (icon) { icon.x = icon.x + checkBox.measuredWidth - 6; } label.x = label.x + checkBox.measuredWidth - 6; }
}
} } //CheckedTreeItemRenderer2
Listing9.12ThesecondversionoftheCheckTreeItemRenderer Wewanttoputourcheckboxinfrontoftheicon.However,ifthetreeitemchoosesnottodisplay theicon,wewantthecheckboxinfrontofthelabel.Let’spositionthecheckbox: checkBox.x = (icon)?icon.x:label.x;
Thenwe’llsetthecheckboxsizetoitsmeasuredsize(everycontrolhasone): checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight);
Nowlet’smovetheiconandthelabeltotherightforthewidthofthecheckbox: if (icon) { icon.x = icon.x + checkBox.measuredWidth ; }
390
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation label.x = label.x + checkBox.measuredWidth;
This second version of CheckedTreeItemRenderer produces the visual in Figure 9.5.The empty boxeshavemovedaside,butnowthere’sagapbetweenthecheckboxandtheicon:
Figure9.5Thesecondversionofthecheckboxedtreewithgaps
Aminorcorrectionadjustingthewidthwillremovethegaps: override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
}
super.updateDisplayList(unscaledWidth, unscaledHeight); if (checkBox) { checkBox.x = (icon)?icon.x:label.x; checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight); if (icon) { icon.x = icon.x + checkBox.measuredWidth - 6; } label.x = label.x + checkBox.measuredWidth - 6; }
Thishard-codednumbersixdoesn’tlookstoogood.ButtheFlexCheckBoxclassextendstheclass RIAWITHADOBEFLEXANDJAVA
391
CHAPTER9
Buttonandwhenwewrotethischaptertheclassmx.controls.Button.ashadthefollowing: package mx.controls { public class Button extends UIComponent implements IDataRenderer, IDropInListItemRenderer, IFocusable, IlistItemRenderer { … override protected function measure():void { super.measure(); … // Pad with additional spacing, but only if we have a label. if (label && label.length != 0) w += extraSpacing; else w += 6; //This is our hardcoded value measuredMinWidth = measuredWidth = w; measuredMinHeight = measuredHeight = h; } } }
We’re sure this will be fixed in the next version of Flex, so we are merely compensating for the w+=6. Thelastmissingdetailissizing.That’sdonewiththemeasure()method,whichsetsthecomponent’sdefaultsizeand,optionally,suggeststhecomponent’sminimumsize.Thismethodiscalled asaresultofoneormoreinvalidateSize()callshappeningbeforethescreen’snextrefresh.Inparticular,methodaddChild()callsinvalidateSize()andsoresultsinmeasure(). Ourimplementationofmeasure()isprettystraightforward;we’lladdthewidthofthecheckboxto thetotalwidthoftherendereditem: override protected function measure():void { super.measure(); if (checkBox) { measuredWidth = measuredWidth + checkBox.measuredWidth -6; } }
392
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation
Figure9.6Thesecondversionofthecheckboxedtree
TheDataBindingofCheckboxes Doyouwanttoseeakaleidoscopeofcheckboxes?Dothis:checkonecheckboxthenresizeyour screensothetreehasaverticalscrollbarandwatchhowduringthescrollingthecheckeditemspop uprandomly.Thereasonissimple:wedidn’ttakecareoftheproperdisplayofthedatamodelvalues,andwealsoforgotaboutchangingthedatamodelvalueswhentheuserclicksonacheckbox. Let’stakecareoftheuserinputfirst.We’llregisterthelistenertothe“click”eventimmediatelyafter thecheckboxisaddedtotherenderer: override protected function createChildren():void { super.createChildren(); if (!checkBox) { checkBox = new CheckBox(); checkBox.styleName = this; addChild(checkBox); checkBox.addEventListener(MouseEvent.CLICK, }
}
checkBoxClickHandler);
RIAWITHADOBEFLEXANDJAVA
393
CHAPTER9
Thelistener–checkBoxClickHandler()–hastoreflectthechangestothecheckbox’sselectedpropertyinthecheckedpropertyoftheappropriatedataProvider’sitem.Howdowegettotheitem? Goodquestion.Aninstanceofdrop-initemrenderershasapropertylistData.(Apairofgetterand setter methods for listData property is mandated by the IDropInListItemRenderer interface.) In caseofTreeitemrenderers,referencetothedataProvider’sitemcanbefoundaftercastinglistData toTreeListData,asshownbelow: private function checkBoxClickHandler(event:MouseEvent):void{ var tld:TreeListData = TreeListData(listData); var item:Object = tld.item; item.checked = checkBox.selected; }
Beware:thistechniqueworksonlyaslongasitemsofthetree’sdataProviderhavepropertychecked or are outright dynamic classes. Otherwise, an assignmentto item.checkedwillcausearuntime exception. Withtheuserinputhandled,let’slookattheproperdisplayoftheitemstate.Thenaturalplaceto handleitisinthesetterofthelistData.We’llsettheselectedpropertyofthecheckBoxtotruewhen theunderlyingitem.checkedistrue.Intheremainingcases–item.checkedisfalseanditem.checked isundefined–wesetcheckBox’sselectedtofalse: public override function set listData(value:BaseListData):void { var checked:Boolean = false; if (value!=null) { var item:Object = TreeListData(value).item; if (item.checked == true) { // can be undefined as well checked = true; } if (checkbox) checkBox.selected = checked; } super.listData = value; } }
Here’sthecompletelistingofthecustomrendererforthecheckboxedtree: package com.theriabook.controls { import mx.controls.treeClasses.*; import mx.controls.*; import flash.events.*; import mx.controls.listClasses.*
394
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation public class CheckedTreeItemRenderer extends TreeItemRenderer { public var checkBox:CheckBox; override protected function createChildren():void { super.createChildren(); if (!checkBox) { checkBox = new CheckBox(); checkBox.styleName = this; addChild(checkBox); checkBox.addEventListener(MouseEvent.CLICK, checkBoxClickHandler); } } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if (checkBox) { checkBox.x = (icon)?icon.x:label.x; checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight); if (icon) { icon.x = icon.x + checkBox.measuredWidth -6; } label.x = label.x + checkBox.measuredWidth -6; }
} override protected function measure():void { super.measure(); if (checkBox) { measuredWidth = measuredWidth + checkBox.measuredWidth -6; } }
private function checkBoxClickHandler(event:MouseEvent):void{ var tld:TreeListData = TreeListData(listData); var item:Object = tld.item; item.checked = checkBox.selected; } RIAWITHADOBEFLEXANDJAVA
395
CHAPTER9
}
public override function set listData(value:BaseListData):void { var checked:Boolean = false; if (value!=null) { var item:Object = TreeListData(value).item; if (item.checked == true) { // the item can be true, false or undefined checked = true; } if (checkBox) checkBox.selected = checked; } super.listData = value; } }
Listing9.13ThefinalversionofCheckedTreeItemRenderer YoucantestouritemrendererwiththedemoapplicationCheckedTreeDemopresentedearlierin Listing9.10andFigure9.3.Thekaleidoscopeofcheckboxesisgone;positioning,sizing,andcheckboxvaluesarealltakencareof.
Summary InthischapterweshowedyouhowtoasynchronouslypopulateTreecontrolsfromremotesources, wherehierarchyofthedataisnotpre-built,butratherdynamicand,possibly,composite.ThecustomTreecontrolthatwebuiltcanbeappliedforausecasewithanynumberoflevelsbychangingtheserver-sidetreeAssemblerandDAOclasses.Wealsodemonstratedthecompleteprocessof extendingastandarditemrendererfortheFlexTreecontrol.Allinall,weshowedhowstandard Flexframeworkcontrolscanbeenhancedtoprovidedeveloperswithsimpleyetpowerfulcomponents.
396
RIAWITHADOBEFLEXANDJAVA
TreeswithDynamicDataPopulation
RIAWITHADOBEFLEXANDJAVA
397
398
RIAWITHADOBEFLEXANDJAVA
CHAPTER
10
WorkingwithLargeApplications
RIAWITHADOBEFLEXANDJAVA
399
CHAPTER10
WorkingwithLargeApplications
Inthischapterwe’llcoverhowtosetuplargeapplicationsintendedforWebor,morebroadlyspeaking, distributeddeployment.Asanexamplelet’sconsideranenterpriseapplicationthatconsistsofhundredsofscreens,reports,forms,anddashboards.Accordingly,aboutadozenengineersspecializingin GUIs,frameworks,datalayers,andbusinessdomainsareworkingonthisapplicationinparallel. Largemonolithicapplications,thewayFlexbuildsapplicationSWFsbydefault,haveseveralproblems:theytakealotoftimetobuildoneachchange,theydonotsupporttheisolationoftwoteams workingontwoindependent“portlets”,theydonotallowtheincrementaldeliveryofpatchesto thesystems,andtheydonotsupporttheindependentdeliveryoflibrariesofreusablecomponents withoutrebuildingtheentireapplication. We’llshowhowtoaccommodatetheserequirementsemphasizingtheproductivityofteamdevelopmentanddeploymentflexibility.Butfirstlet’sreviewthedeploymentscenariosfromthebusinesspoint-of-viewindetail.
DeploymentScenarios Throughoutthischapterwe’llusethetermpatches,whicharethefixesmadetoanapplicationbetweenreleases.Add-onsarethepartsoftheapplicationthataretypicallyaddedovertime.Paches aswellasadd-onsblendseamlesslyintothehostingapplication,bothvisuallyandprogrammatically. In some cases the application build may not even“know” about specific add-ons since they’re different for each user type. For example, an enterprise application can reveal more screens or morefunctionalitywithinthesescreenstointernalusers.Inthiscasewetalkaboutportlet-style add-ons. Plug-insareindependentapplicationsthatdon’tsharethelook-and-feelofthemainapplication. Nointensiveinteractionbetweenthemainapplicationandplug-insisexpected.
400
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
ApplicationDomains101 Asmuchaswedon’tliketoduplicatetheworkdonebytheAdobeFlexdocumentationteam,we havetocoverthesubjectofApplicationDomainssinceit’sessentialtothischapter.So,herearethe facts. TheFlashPlayerloadsclassdefinitionsfromSWFfilesintotheinstancesoftheflash.system.ApplicationDomainclass–akaapplicationdomains.Applicationdomainsareorganizedinahierarchy withsystemdomaincontainingallapplicationdomains.Ifaclassisalreadyloadedintoaparent domain,loadingadifferentdefinitionintothechilddomainwillhavenoeffect.Ifthissoundsfamiliar,youmayhaveworkedwithJavaClassLoaders. Don’tmistakeapplicationdomainsforsecuritydomains.ThelatterarerelevantforissueslikesubloadinganSWFfilefromadifferentserverandareoutsidethescopeofthischapter. Let’s move on. There are three classes in the Flex library that facilitate SWF loading: flash. display.Loader, mx.controls.SWFLoader, and mx.modules.ModuleLoader.With each of them you get two choices of where you want to load the classes: a new (child) domain or an existing applicationdomain.ModuleLoaderhasanapplicationDomainproperty.IfyousetittonewApplic ationDomain(ApplicationDomain.currentDomain),newclasseswillbeloadedintoachildofthe currentDomain(thedomainwherethemainapplicationruns);toenforceloadinginthesamedomain you should set applicationDomain’s value to ApplicationDomain.currentDomain. In each casewithLoaderandSWFLoader,youcontrolthetargetdomainbysimilarlyassigningtheobject’s loaderContext.applicationDomain. Wehavementionedtheovershadowingofchildren’sclassdefinitionsbytheparentones.Accordingly,whenyoubringexistingFlexsubsystems(perhapsevenwritteninadifferentversionofFlex) underacommonapplicationumbrella,itmakessensetoresorttoaseparateapplicationdomain, i.e.,achildofthesystemdomain.Ontheotherendofthespecter,ifyouneedtodynamicallyload resourceslikeDataGriddefinitions,youwillbebetteroffwiththesamedomainwherethemain applicationisrunning.
RuntimeSharedLibraries101 Flex documentation defines Runtime Shared Libraries (RSL) as “a library of components.” We wouldliketostartwiththeclarificationthatRSLisnotafilebutapatternofusinganSWFfilefrom withinanotherSWFfile. Specifically,whenyoucompilewithRSLs,FlexgeneratescodetopreloadtherelevantSWFsduring theapplication’sbootstrap.Tobeexact,definitionscontainedintheSWFareloadeddirectlyinto theapplicationDomainofthehostingapplication. Nowhowdoestheapplication’sbootstrapknowwhichSWFfilesaretobepre-loaded? Hereisananswer.Let’sassumethat: RIAWITHADOBEFLEXANDJAVA
401
CHAPTER10
a. YoumadethefileFlexLibrary.SWC(runningthecompccompilerexplicitlyorjustbyrebuildingtheFlexBuilder’sLibraryProject). b. You’vecreatedthefileFlexApplication.mxml,whichreferstocomponentsfrom FlexLibrary.SWC. c. WhilecompilingFlexApplication.mxmlyouinstructedthemxmlccompilerthat FlexLibrary.SWCcontainsanimageofanSWFtobepre-loadedduringthebootstrap(thiswill beexplainedlaterinthischapter). Then, the corresponding ActionScript file generated by the mxmlc compiler will have the code fragmentshowninListing10.1.You’llfindthisandotherfilesinthegeneratedfolderofyourapplicationprojectonceyousetthecompiler’soptiontokeep-generated-actionscript=true: public class _FlexApplication_mx_managers_SystemManager extends mx.managers.SystemManage r implements IFlexModuleFactory { public function _FlexApplication_mx_managers_SystemManager() { super(); } override public function info():Object { return { “currentDomain”: ApplicationDomain.currentDomain, “layout” : “absolute”, “mainClassName” : “FlexApplication”, “mixins” : [“_FlexApplication_FlexInit”, ......] , “rsls” : [{url: “FlexLibrary.swf”, size: -1}] }; } } }
Listing10.1ThefragmentofthegeneratedSystemManagerclassforFlexApplication.mxml Listing10.1containsthecodeoftheSystemManagerclassoftheFlexApplication.FlexSystemManager isthefirstdisplayclasscreatedwithinanapplicationandtheparentofallotherdisplayableobjects. SystemManageralsocreatesthemx.preloaders.PreloaderthatloadsSWFfiles. PleasenotethatFlexLibrary.swfinandofitselfisnotanRSL.Aswesaidabove,RSLisausagepatternratherthanafile.WhatmakesFlexLibrary.swfpartofthispatternistheintenttopre-loadit duringtheapplicationstartupcommunicatedbyustothemxmlccompiler. Alsonotetheline: “currentDomain”: ApplicationDomain.currentDomain,
402
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
ThisiswhyclassdefinitionsfromtheFlexLibrary.swfareloadedintothesamedomainwherethe definitionoftheapplicationclassesbelong.AccordinglywefindtheRSLtechniqueespeciallyusefulfordeliveringvariouspatches,whichshouldbeloadedpriortoanyotherclassdefinitions.RSLs arealsothebestwaytopackagecomponentlibrariesandresources.
SWFsandSWCs:What’sUndertheHood HowdoourSWCfilesrelatetoSWFs?AnSWCisanarchivefile,muchlikeZIPorJAR,andcontains library.swfandcatalog.xmlfiles.Thelatterdescribesthehierarchyofdependenciesfoundtheformer; library.swf may, but it won’t nessesarily become FlexLibrary.swf (depending on the selected LinkTypedescribedbelow).
Figure10.1ThecontentoftheFlexLibrary.SWCfile
WhenwecompileFlexApplication.mxmlcontainingreferencestoFlexLibrary.SWCinthelibrary searchpath,therearethreelinktypestochoosefrom: •
External:Thecatalog.xmlintheFlexLibrary.swcwillbeusedtoresolvereferences;however thedefinitionscontainedinlibrary.swfwon’tbeincludedinthebodyoftheFlexApplication. swf.TheExternallinktypeassumesthatbythetimeFlexApplicationneedstocreateinstances ofclassesfromthelibrary.swfpartthedefinitionsfortheseclasseswillbeaccessiblefromthe currentDomain.
•
RSL:Thecatalog.xmlintheFlexLibrary.swcwillbeusedtoresolvereferences;thedefinitionscontainedinlibrary.swfwon’tbeincludedinthebodyoftheFlexApplication.swf.Sofar soundslikeExternal,right?Here’sthedifference:alldefinitionsoriginallycontainedinthe library.swfpartwillbeupfront-loadedintothemainapplicationDomainduringapplication startup.
•
Merge-in:Definitionscontainedinlibrary.swfgetembeddeddirectlyintoFlexApplication. swf,butwait...onlythosethatareexplicitlyreferencedbytheapplicationcode.Thisisa defaultoptionforstaticallylinkedapplicationsandguaranteesthatthedefinitionsofall RIAWITHADOBEFLEXANDJAVA
403
CHAPTER10
referencedclassesaswellastheclassestheydependonareloadedintothemainapplicationDomain. AMerge-inscenarioisoftencalledstaticlinking,whileExternalandRSLarecasesofdynamiclinking. SupposewewentwithdynamiclinkingviaRSL.AsillustratedinListing10.1,thismeanspre-loadingtheFlexLibrary.swf.Here’sthequestion:wheredowegetthisFlexLibrary.swffrom?Underone scenariowecanletFlexBuilderextractandrenamethelibrary.swffromtheFlexLibrary.swc.InFlex Builder(projectProperties>FlexBuildPath>LibraryPath)thisoptioniscalledAutoextract.Alternatively,wecouldhavedeclinedauto-extractingandunzippedtheSWFfromtheSWCourselves. Aswe’llshowlater,there’syetanotherwayofexplicitlycontrollingtheupfrontbuildofFlexLibrary. swf. We’llillustratethesecasesinthenextsection.
MakingtheFlexLibrary.swc Let’smakeanSWCinFlexBuilderbycreatinganewFlexLibraryProject.Theonlycomponentwe’re goingtoaddtothisSWCistheCustomPanelfromListing10.2.TheCustomerPanelenumerates theinstancesofitselfandimprintsthenumberoftheinstanceaspartofitstitle,usingthevariable instanceNumberthatwe’vedeclaredbindable:
Listing10.3FlexApplication.mxml Thenwe’lllinkourlibrary(seeFigure10.4),compile,andruntheapplication.Theoutputwindow willlooklikethis:
RIAWITHADOBEFLEXANDJAVA
405
CHAPTER10
Figure10.3ACustomPanel
Figure10.4illustratesthedetailsofaddingourrecentlycreatedlibraryFlexLibrary.swctotheproject’sLibraryBuildPath.Thedefaultlinktypeistomerge-intheSWC’scontent(tochangethelink type,ifneeded,selecttheLinkTypelineandpressthebuttonEditonthescreenbelow):
406
Figure10.4LinkingFlexLibary.swctoFlexApplication–“Mergedintocode”
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
MergingthecontentsofSWCresultsinanoptimized,non-overlappingsizeofthemonolithicapplication.AsshowninFigure10.5,thesizeofsuchaself-sufficientFlexApplication.swfis123KB.
Figure10.5TheFlexApplicationdeploymentfolderafter merging-inFlexLibrary.swc
Nowlet’schangetheLinktypetoRSL.We’llacceptthedefaultvalueofAutoextract=true:
Figure10.6LinkingFlexLibary.swctoFlexApplication–“RSL”
RIAWITHADOBEFLEXANDJAVA
407
CHAPTER10
AsaresultoftheAutoextract,thefileFlexLibrary.swfwillappearadjacenttotheFlexApplication. swf.ThesizeoftheFlexLibrary.swfwillbe236K,nearlyasmuchastheentireFlexLibrary.swc,but thesizeoftheFlexApplication.swfwilldecreaseto40K:
Figure10.7ThefileviewoftheFlexApplicationdeployment folderafterauto-extractingtheFlexLibrary.swf
NowyoucanseewhythedefaultLinkTypeinFlexisMergeintocode.Thisensuresthesmallestsize oftheresultingmonolithicSWFthatcarriesonlythecodethatwasdeemedrelevantduringthe compilationprocess. Naturally, the total of 236K + 40K takes two times longer to download then downloading of the staticallylinked123K.However,onceyouhaveafamilyofthreeapplicationstoofferthesameuser, allapproximatelythesamesizeandallreusingthesameFlexLibrary.swf,itbecomes(236+3*40) versus3*123andyoubreakeven. Thesize/downloadconsiderationsarelessrelevantonthefastconnectionsand,perhaps,arenot relevantatallinscenarioswhereyoucancountonthebrowsercachetokeepSWFfilesloadedfor thenextrunoftheapplication.Atypicalexamplewherethetimeoftheinitialdownloadisnotan issuewouldbeacorporateenvironment,butthenagain,someadministratorssetpoliciestowipe outthebrowser’scacheonuserlogoff.
StaticversusDynamicLinking:DevelopmentPerspective Let’schangethesubjectfromdownloadtimetoadeveloper’sproductivity,afactorfarmoreimportantforenterpriseapplications. Let’slookatenterpriseapplications.Drivenbyuserrequirements,theseapplcationstendtochange
408
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
frequently,growinginfunctionalityand,accordingly,size,withphaseddeliverytotheusers.Asanapplicationgetstotensofmegabytes,thetimerequiredtobuildthemonolithicSWFbecomesanoticeablesetbacktodeveloperproductivity.Theproblemgetsmagnifiedinateamdevelopmentwhereit translatesintomanyman-hourswastedeveryday. And,regardlessofsize,let’slookattheusecaseofportlet-styleadd-ons.Thesearemodulesthatare simplyimpossibletoreferencestaticallyinadvance.Infact,theyarenotevensupposedtobepreloadedatall:unlikeRSLstheygetloadedondemand(moreonthatlaterinthechapter). Allinall,weneedtobeablebreaktheapplicationintoasetofmodulesthatcanbebuiltindependentlyandlinkeddynamicallyatruntime.
So,YouSayDynamicLinking? Bynowthereadermaysay,“OK,Igotthemessage,I’llgowithRSLstomodularizemydevelopment withdynamiclinking.”Notsofast.LikeancientAchilles,thesemightyRSLshaveasmallweakspot: theirwell-beingdependsonstaticlinkagefromthemainapplication. Oh,butdoesn’tthatruinthehopeofdynamiclinking?Theanswerisno,andtheexplanationisjust aroundthecornerinthenextsection. First,however,let’screateanapplicationtoexposetheproblem.We’llbuildaFlexApplication2application.Unlikeourpreviousexample,itwon’tcontainstaticreferencestoCustomPanel.Instead,FlexApplication2willcreateinstancesofaCustomPanel(oranyotherobjectforthatmatter)dynamically, givenanameoftheclassdefinitionasit’sdoneinthefunctioncreateComponent()ofListing10.4:
Listing10.4FlexApplication2.mxml
RIAWITHADOBEFLEXANDJAVA
409
CHAPTER10
Ifyouruntheapplicationandclickthe“CreatePanel”button,thecodeterminatesabnormallyas showninFigure10.8.
Figure10.8Anerrorrelatedtoalackofinitializingthelibrary
ThereasonforthiserroristhatmanytimesmxmlccomplementsclasseswithadditionalinitializationcodeattheSystemManagerlevel.Butiftherelevantclassesarecompletelyshieldedfrom mxmlcit’sabsolvedfromtakingcareofthem.Let’sexplainthisindetail.Pleasehaveanotherlook atthegeneratedSystemManagercorrespondingtoFlexApplicationfromListing10.3. package { import mx.managers.SystemManager; import flash.utils.*; import flash.system.ApplicationDomain; import mx.core.IFlexModuleFactory; public class _FlexApplication_mx_managers_SystemManager extends mx.managers.SystemManage r implements IFlexModuleFactory { public function _FlexApplication_mx_managers_SystemManager() { super(); } override public function info():Object { return { “currentDomain”: ApplicationDomain.currentDomain, “layout” : “vertical”, “mainClassName” : “FlexApplication”, “mixins” : [“_FlexApplication_FlexInit”, “_activeTabStyleStyle”, … “_ControlBarStyle”, “_PanelStyle”, “_CustomPanelWatcherSetupUtil” ] ,
410
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications “rsls” : [{url: “FlexLibrary.swf”, size: -1}] }; } } //_FlexApplication_mx_managers_SystemManager }
Listing10.5TheGeneratedSystemManagerclassforFlexApplication.mxml IfwecomparethisSystemManagerwiththeonegeneratedforourlatestexample–FlexApplication2–we’llseethatinthelattercasethemixinsarrayisshortofthethreevalues:“_ControlBarStyle,”“_PanelStyle,”and“_CustomPanelWatcherSetupUtil.” Theclassesreferencedinthemixinsarraytakepartintheinitializationsequenceoftheapplication upontheinitialload.Inparticular,CustomPanelWatcherSetupUtilistheclassthatfacilitatesthe bindingforthevariableinstanceNumberofCustomPanel.InthecaseofFlexApplication2,thispart oftheinitialization“getsforgotten.” SystemManager files __mx_managers_System.Manager.as are not the only onesaffected.Thenextinterestingsetoffilestolookafterare_FlexInit.as.For example, in Chapter 8 we used to annotate the EmployeeDTO class with the metadata keyword RemoteClass: [RemoteClass(alias=”com.theriabook.composition.dto.EmployeeDTO”)]
BelowistherelevantpartfromageneratedFlexInitclass: package { [Mixin] public class _SimpleAutoCompleteWithDynamicDataDemo_FlexInit { . . . . public static function init(fbs:IFlexModuleFactory):void { . . . . flash.net.registerClassAlias( “com.theriabook.composition.dto.EmployeeDTO”, com.theriabook.composition.dto.EmployeeDTO ); . . . . } } // FlexInit } // package
Andagain,theregistrationsnippet RIAWITHADOBEFLEXANDJAVA
411
CHAPTER10 flash.net.registerClassAlias( “com.theriabook.composition.dto.EmployeeDTO”, com.theriabook.composition.dto.EmployeeDTO );
NowimaginethescenarioinwhichyourRSLdefinesEmployeeDTOandtheclassesinRSLanticipateusingit,buttheapplicationdoesnotexplicitlymentionEmployeeDTOanywhere.Whathappens?Theaboveinitializationcodedoesnotgetgeneratedatall. Allinall,MXMLfilesaswellasmetadata-annotatedActionScriptclassesmightnotgettheexpectedinitializationsupportifyouputtheminRSLanddon’texplicitlyreferencetheminthecalling application.
Self-InitializingLibraries–Applications Enoughoftheproblems,let’sgetbacktopositivethinking.Here’sashortdescriptionofthesolution: Wewillpolitelydeclineasuggestionto“AutoExtract”SWFfromSWC.Wewillbemakingtheright SWFourselvesviaanAntbuildfilethatcontrolshowwebuildtheSWF.Namely,wewillbuilditas anapplicationtoguaranteethat,effectively,ourlibrarywillinitializeitself.AsfarasSWCgoes,we willrestrictitsroletomerelysupportingthenameresolutionduringthecompilationofthemain application. Tothatend,we’lladdthefollowingFlexLibraryMain.xmlapplicationandFlexLibraryMain.asclass (Listings10.6and10.7,respectively):
Listing10.9ResourcesMain.mxml. ThefileResourcesMain.asenforcesthestaticlinkageoftheclassesEmployeeComboBoxResources andEmployeeDTOandregistersthealiasoftheEmployeeDTOclass: // ResourcesMain.as package { import mx.core.SimpleApplication; import com.theriabook.composition.dto.EmployeeDTO; import com.theriabook.resources.EmployeeComboBoxResource; import flash.net.registerClassAlias; public class ResourcesMain extends SimpleApplication { public function ResourcesMain() { RIAWITHADOBEFLEXANDJAVA
417
CHAPTER10 // Custom library initialization code should go here registerClassAlias( “com.theriabook.composition.dto.EmployeeDTO”, EmployeeDTO ); trace(“Resources.swf has been loaded and initialized”); } // Static linking of the required classes should go here private var linkage:Object = { t1:EmployeeComboBoxResource }; }//ResourcesMain
Listing10.10ResourcesMain.as Thethirdfile–build.xml–facilitatesbuildingResources.swfwithAntandmxmlc.Pleasenotethat unliketheexampleinListing10.8,wherewereferencedtheFlexSDKSWCfiles,herewepointto WEB-INF/flex/libsfolder.Thereasonisthatweneedfds.swc,whichisn’tapartoftheFlexSDK.Also notethatwedirecttheResources.swfoutputfilestraightintothedeploymentfolderoftheComboBoxCodeapplication:
Listing10.11TheAntbuild.xmlfiletobuildResources.swflibrary
418
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
Whenwerunthisbuild.xmlasanAnttask,itproducesaResources.swffileof20,851bytes. Atthesametime,let’snotforgettheoriginalpurposeofResources.swc–itshouldhelpinresolving allreferencesduringtheapplication’sbuild.That’swhywecheckoffalltheclassestheapplication mightneedasshowninFigure10.13.TheoutputfolderfortheResources.swcwillbeWEB-INF\ flex\user_classes:
Figure10.13Classesfor.flexLibPropertiesfortheResourcesproject
Thesecheckboxescorrespondtothefollowingcontentsofthe.flexLibPropertiesfile:
RIAWITHADOBEFLEXANDJAVA
419
CHAPTER10
Listing10.12TheflexLibPropertiesAntfileoftheResourceproject Asyoucansee,outofthethreefileslistedin(DOT)directlyinfrontofflexLibProperties,ourResources.as,showninListing10.10,explicitlyreferencesEmployeeComboBoxResourceandEmployeeDTO.Wedidnothavetoregisterthebaseclass–ComboBoxResource–asthisisthecompiler’s job. Nowlet’stakecareoftheapplicationsettings.FortheComboBoxCodeprojectwe’llsettheLink TypeofallthelibrariesexceptResources.swctoRSLwithAutoextract,whileResources.swcwillbe setasRSLwithoutAutoextractasinFigure10.14.
Figure10.14ThelibrarypathfortheComboBoxCodeproject
Finally,we’llcommentoutofthelinkagevariableintheapplication,sincethepre-loadofResources.swfthatwehavebuiltasaself-initializinglibrarywillautomaticallyloadtheEmployeeComboBoxResourceclassinthecurrentDomain.
422
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
Listing10.14AuthorsGrid.mxmlfromFlexLibraryproject We’llbeginworkingonourapplicationbyaddingtheAuthorsGridtotheFlexLibraryprojectand registeringitwiththeFlexLibraryMain.asclass: // FlexLibraryMain.as package { import mx.core.SimpleApplication; public class FlexLibraryMain extends SimpleApplication { public function FlexLibraryMain() { // Custom library initialization code should go here trace(“FlexLibrary.swf has been loaded and initialized”); } // Static linking of the required classes should go here private var linkage:Object = { t1:CustomPanel, t2:AuthorsGrid }; }//FlexLibraryMain } }
Listing10.15TheResources.asfilewiththestaticlinkageofAuthorsGrid AtthispointwecanreruntheAnt’sbuild.xmlfile,whichwillplacetheupdatedFlexLibrary.swfin thesamefolderthatweexpectourFlexApplication3torunfrom. Let’s“prepare”thisapplicationbyremovingtheFlexLibrary.swcfromtheFlexApplicationproject’s Build Path. Up till now we’ve been pre-loading the FlexLibrary.swf the RSL way, which we don’t wantitanymore.Accordingly,wewillcommentoutthestaticreferencetoCustomPanelinsidethe FlexApplication.mxml,otherwisetheproject’sbuildwillfail:
DataGrid { fontFamily: Arial; fontSize: 14; headerStyleName:”dgHeader”; } .dgHeader {
426
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
}
fontFamily: Arial; fontSize: 14; fontWeight:bold;
Listing10.19FlexApplication3.mxml Let’s run the application in the debug mode to see the debugger’s trace messages.This is what you’llseeintheconsolewindowwhentheapplicationstarts: [SWF] C:\TheRIABook\code\applications\FlexApplication\bin\utilities.swf - 1,827 bytes after decompression [SWF] C:\TheRIABook\code\applications\FlexApplication\bin\flex.swf - 49,833 bytes after decompression [SWF] C:\TheRIABook\code\applications\FlexApplication\bin\FlexApplication3-debug.swf 104,664 bytes after decompression [SWF] C:\TheRIABook\code\applications\FlexApplication\bin\framework.swf - 2,235,667 bytes after decompression
Listing10.20ConsolewindowmessageswhenFlexApplication3starts
RIAWITHADOBEFLEXANDJAVA
427
CHAPTER10
ThesemessagesreflectthefactthatalloftheabovelibrariesaremarkedasRSLswith“Autoextract.” Nowlet’spressthe“1.LoadLibrary”button.Thefollowingoutputwillgototheconsole: open[Event type=”open” bubbles=false cancelable=false eventPhase=2] progress[ProgressEvent type=”progress” bytesLoaded=0 bytesTotal=16440] progress[ProgressEvent type=”progress” bytesLoaded=8192 bytesTotal=16440] progress[ProgressEvent type=”progress” bytesLoaded=16384 bytesTotal=16440] progress[ProgressEvent type=”progress” bytesLoaded=16440 bytesTotal=16440] [SWF] C:\TheRIABook\code\applications\FlexApplication\bin\FlexLibrary.swf - 36,126 bytes after decompression init[Event type=”init” bubbles=false cancelable=false eventPhase=2] httpStatus[HTTPStatusEvent type=”httpStatus” bubbles=false cancelable=false eventPhase=2 status=0] complete[Event type=”complete” bubbles=false cancelable=false eventPhase=2] Module file://C:\TheRIABook\code\applications\FlexApplication\bin\FlexLibrary.swf complete. FlexLibrary.swf has been loaded and initialized
Listing10.21Consolemessagesinresponsetolibraryloading Atthispointwecanpressthebutton“2.ShowLibraryGrid.”Thegridwiththedatawillappear exactlyasshowninFigure10.16. Congrats!We’vejustloadedavisualdynamicobjectAuthorsGridthatcanaccessotherobjectsand beaccessedthesamewayasanyotherclass.Inparticular,AuthorsGridadherestoglobalstyles whileitspropertiesandmethodsaredirectlyaccessiblefromtheapplicationclass.
EmbeddedApplicationsandtheSWFLoaderObject Inthissectionwe’lldiscussplug-ins.Fortunately,Flexhasaperfectout-of-the-boxsolutionforthis typeofintegrationviatheSWFLoadercontrol,whichletsyouembedanySWFfilethatrepresents Flex2.0applicationorjustanyFlashmovie.Let’sembedone application,InnerApplication.swf, intoanotherapplication,OuterApplication.swf. HereisthelistingofInnerApplication.mxml,whichshowsthePanelwithtwolabelcontrolsona lightpinkbackground:
C:/Program Files/Adobe/Flex works/libs/playerglobal.swc C:/Program Files/Adobe/Flex works/libs/framework.swc C:/Program Files/Adobe/Flex works/libs/utilities.swc C:/Program Files/Adobe/Flex works/libs/flex.swc
Builder 2 Plug-in/Flex SDK 2/frameBuilder 2 Plug-in/Flex SDK 2/frameBuilder 2 Plug-in/Flex SDK 2/frameBuilder 2 Plug-in/Flex SDK 2/frame-
RIAWITHADOBEFLEXANDJAVA
429
CHAPTER10
Listing10.23InnerApplication-config.xml Let’smarktheInnerApplicationasthedefaultoneandbuildtheproject.ThiswillbuildanInnerApplication.swf. Nowlet’sfocusontheOuterApplication.Firstandforemost,itwilltohaveaninstanceofSWFLoader,whichloadstheInnerApplication.swf:
Next,itwillhavealabel,whosevaluegetsreplacedbythevalueobtainedfromthelabel_1ofthe InnerApplicationwhenthe“Readinnerlabel”buttonisclicked:
Itwillalsocontainatextfield,wherewe’lltypesometext,andtwobuttonstopassthistextdirectly intotheInnerApplication’slabel_1andthecorrespondingvaluevariable:
ThedefaultsettingoftheloaderContextoftheSWFLoaderistoloadnewdefinitionsintothechild domain.Accordingly,toaccessthelabel_1insidetheInnerApplicationwemaydosomethinglike this: var systemManager:SystemManager = SystemManager(swfLoader.content); var innerApplication:Application = Application(systemManager.application); trace(innerApplication[“label_1”].text);
(FlexprovidesseveralotherwaysOuter-andInner-applicationscaninteroperate,suchasSharedObjects,LocalConnection,andExternalInterface.WediscussthelasttwoinChapter15.) ThefulllistingoftheOuterApplication.mxmlispresentedbelow:
[Bindable] public var value:String = “From InnerModule with love!”; public function getProperty(name:String):* { if (name==”value”) return value; else return undefined; } public function setProperty(name:String, newVal:*):void { if (name==”value”) value=newVal; }
RIAWITHADOBEFLEXANDJAVA
433
CHAPTER10
Listing10.23InnerModule.mxml You’llnoticethatourmoduleimplementstheIHeyModuleinterface,butpleasedon’tgettheidea thatmoduleshaveanythingtodowithinterfaces.Likewise,modulesandapplicationsloadedby SWFLoader are free to implement any interface, much as OuterApplication and OuterModuleLoader(laterinthissection)arefreetoignoreaninterfaceaslongasthenamesofthepublicpropertiesareknownandthereisanabundanceofdoublequotes. WemodeledInnerModule-config.xml,Listing10.24,afterInnerApplication-config.xml.Sincewe externedtheFlexframework,thebuildresultsina12KsizeoftheInnerModule.swf.
RIAWITHADOBEFLEXANDJAVA
435
CHAPTER10
Listing10.26OuterModuleLoaderapplication WhenyourunOuterModuleLoader,itslookandbehaviorwillbeindistinguishablefromOuterApplication,Figure10.18. Finally,wepresentsamplesofusingtheIHeyModuleinterfacebyInnerApplicationandOuterApplication,Listings10.27and10.28,respectively.
Listing10.28VersionofOuterApplicationthatmakesuseoftheIHeyModuleinterface
RIAWITHADOBEFLEXANDJAVA
437
CHAPTER10
WhenSizeMatters Flexcompilerssupportthe-link-reportoption.Itletsyouspecifyafiletobepopulatedwiththe linkerdependenciesfoundduringthebuildofyourSWF.Thereisamatching-load-externscompileroptionthatletsyouspecifytheclassesyoudon’twanttolink,butratherprefertoextern.Conveniently,-load-externsanticipatestheinputtobeinexactlythesameXMLformatasisproduced by-link-report. Speakingofmodulesandapplications,justasanexamplecase,thispairofoptionsenablesyouto externforthemoduleeverythingthatwillbeloadedbytheapplication.However,ifyouplantoever reusethesamemoduleforthedifferenthostingapplication,thistechniqueisnotapplicable. Thereisanoppositewaytousethe-link-reportoption.Earlierweexplainedwhyyoushoulduse self-initialized RSLs for component and resource libraries (as opposed to visual plugins, where modulesandapplicationsreign).Ifyourecall,wemanagedtokeeptheseRSLsextremelytinyby externinganentiresetofFlexframeworkSWCfilesundertheassumptionthatthemainapplicationwouldsupplythoseviaAutoExtract.Theonlydownsidehasbeentheindiscriminatingextract oftheentiresetofclasses(resultinginalarge1Mbframework.swfinparticular).Butwhytoleratea wholesaleextract?Ifyouusethecompiler’soption–includeyoucangobacktomerge-inLinkType andhavetheapplicationmergeinalltheclassesrequiredbyyourlibraries. Inthisscenario,whichappliestoself-initializedRSLsaswellasmodules,yourun-link-reporton themoduleorlibraryandinsteadofoptimizingthemodule,youoptimizetheapplication.Noneed torebuildyourlibrariesormodulestosatisfyanindividualapplication–theystay100%reusable. Instead,youtuneyourapplications.4 And,speakingofsize,don’tforgetthe-debugoption.Byturningittofalse,youmaystripupto30% ofthesizetakenbythedebugginginformation.Bythesametoken,youmaywanttorecompile framework.swffromtheAdobeFlexFrameworksourcefilestotakeitssizedowninthefirstplace, priortoresortingto–include.
Summary Inthischapterwewentthroughanumberoftechniquesofutmostimportancewhenitcomesto largeapplicationdevelopment.Weshowedyouhowto: • • • •
438
Breakamonolithicbuildprocessintoasetofsmallerindependentbuilds Enabledeveloperstoworkwithisolatedlibrariesofcomponentsandresources Do“on-demand”loadingoffunctionalitythat’simpossibletoforecastandpackageinadvance Optimizatethetotalsizeoftheapplication’ssize
RIAWITHADOBEFLEXANDJAVA
WorkingwithLargeApplications
Endnotes 1. Awordofadvice:makeadecisionwhereyouwanttokeepyourSWCfilesandsticktoit.AvoidthesituationwheretheSWClinkedinbyyourapplicationisnotthesameonethatyou’remakingwiththeFlex BuilderProject. 2. InFlex2.0.1youcancoordinaterunningtheAntfilewiththebuildoftheSWCbyaddingtheextra “builder”totheproject. 3. Duringthelifetimeofanyresourceprojectorcomponentlibraryproject,thecorrespondingResourceMain.asfilewillneedtobekeptinsyncwiththeprojectcontents.Onewaytoachievethisistoincludea code-generationtargetinyourAntscriptandtreatthecontentsof.flexLibPropertiesasinputdata.Alternatively,toautomatethisandallotherFlexbuild-relatedtasks,youcanusetheFlex2Antutilitydeveloped byFarataSystems,seehttp://www.myflex.orgformoreinformation. 4. ThecurrentreleaseofFlexdoesnotprovideautomationtoolsforthisoptimization,soyoumaywantto createtheautomationprocessyourself.Alternatively,youcanuseFlex2Ant,byFarataSystems,seehttp:// www.myflex.orgformoreinformation
RIAWITHADOBEFLEXANDJAVA
439
440
RIAWITHADOBEFLEXANDJAVA
CHAPTER
11
AdvancedDataGrid
RIAWITHADOBEFLEXANDJAVA
441
CHAPTER11
AdvancedDataGrid
ThesubjectofDataGridcontrol,letaloneadvancedcontrol,hasnolimits.InanyUIframework,the robustnessoftheDataGrid,orwhateverthenamemightbe,dependsonformattingandvalidating utilitiesaswellasawholesuiteofdatainputcontrols:CheckBoxes,ComboBoxes,RadioButtons,all sortsofInputs,Masks,andsoon.Usingtheatricalterminology,theroleofthekingisplayedbyhis entourage.Practicallyspeaking,touchingupontheDataGridistouchinguponalargepartofthe Flexframework.Hencethischapterisaridewithfast-changingscenery. We’llstartourDataGridjourneyupgradingastandardDataGridtoa“destination-aware”control capableofpopulatingitself.Sinceweintroducedthistechniqueinearlierchaptersyoushouldease rightonin.Next,we’lllookatformattingDataGridcolumnsandthatwouldnaturallyleadustoa hiddentreasuryoftheDataGridColumn,which,onceyoustarttreatingitlikeacompanionrather thanadullelementofMXMLsyntax,canhelpyoudoamazingthings. TosetthestagefortheDataGrid’ssatellitecontrols,we’llleadyouthroughmakingareusableFlex librarythatsupportsthemappingofyourcustomtagstothearbitraryhierarchyofimplementation classes.Wewillshowyouthepowerofcomputedexpressionsinplaceofstylessuchascolor,and font,makingourDataGridtrulydata-driven.Wewillbuildasuiteofeffectivecontrolstouseasitem renderers/editors,aswellasstandalone.Thenwewillstepuptheoffenseandshowyouhowthe entiresetsofDataGridColumndefinitions,includingitemeditorsandrenderers,canbedynamicallycomputedbasedonthedata!
MakingDataGridDestination-Aware InChapters7and8weintroducedtheconceptofdestination-awarenessforcollectionsandcontrols.IthelpstoeliminatethetediouseffortrequiredtopopulateyourcontrolswithRemotingor DataServices-based data. Here’s how an MXML application with a destination-aware DataGrid might like if we apply the familiar remoting destination com_theriabook_composition_EmployeeDAO:
/> />
Listing11.2LabelFunctionFormattingDemo.mxml Here’showourformattinglooksonthescreen:
Figure11.2ScreenshotoftherunningLabelFunctionFormattingdemo
FormattingwithExtendedDataGridColumn The labelFunction-based formatting does the job.The price tag is hard-coding labelFunction(s) namesintheDataGridColumndefinitions.Ifyoupackagedasetoflabelfunctionsinthemydomain.LabelFunctionclass,yourcolumndefinitionsmightlooklikethis:
446
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
dataField=”PHONE”
labelFunction=”mydomain.LabelFunctions.
dataField=”SS_NUMBER” labelFunction=” mydomain.LabelFunc-
AmorepragmaticapproachistointroducetheformatStringasanextraattributeforDataGridColumnandhaveitencapsulatetheimplementationdetails.We’retalkingaboutthefollowingalternative:
Implementation of such syntax is within arm’s reach. We just need to extend – well, not the arm, but the DataGridColumn, so that instead of mx:DataGridColumn we would use, say, our fx:DataGridColumn. The mx.controls.dataGridClasses.DataGridColumn is just a respository of stylesandpropertiestobeusedbytheDataGrid.IntheFlexclasshierarchyitmerelyextendsCSSStyleDeclaration.Nothingpreventsusfromextendingitfurtherandaddinganextraattribute.In the case of formatString we delegate the actual job of assigning the value of labelFunction to a helperclass-FormattingManager: public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn { public function set formatString( fs:String ) : void{ FormattingManager.setFormat(this, fs); } }
Waitaminute,wheredidtheFormattingManagercomefrom?Well,we’llgettotheimplementation ofthatclassabitlater.Atthispoint,wehavetoeliminateapossiblenamingcollisionbetweenour to-be-madeDataGridColumnandthestandardmx.controls.dataGridClasses.DataGridColumn.
IntroducingaComponentManifestFile Uptillnowwe’vebeenkeepingfolderswithclassesofourcustomcomponentsundertheapplicationMXMLfilesfolder.Toreferencethesecomponentswe’vebeendeclaringnamespacespointing to some hard-coded albeit relative paths such as xmlns:lib=”com.theriabook.controls.*” or xmlns=”*”.Theproblemwiththisapproachisthatthesenamespacespointtoonefolderatatime. Asaresult,weendupwitheithermultiplecustomnamespacesorawildmixofcomponentsinone folder. Tobreakthespellandabstractthenamespacefromtheexactfilelocation,wehavetousethecomponentmanifestfile.ComponentmanifestisanXMLfilethatallowsmappingcomponentnames to the implementing classes. Below is an example of a component manifest that combines our customDataGridandDataGridColumnlocatedindifferentfolders:
RIAWITHADOBEFLEXANDJAVA
447
CHAPTER11
Tobenefitfromtheuseofthiscomponentmanifestyouhavetocompileyourcomponentswiththe compcorusetheFlexLibraryproject.Tobemorespecific,youhavetoinstructcompctoselectthe URLthatyourapplicationcanlateruseinplaceofthehard-codedfolderinthexmlnsdeclaration. Sowe’llcreateanewFlexLibraryproject–theriabook,wherewewillputthetheriabook-manifest. xmlcontainingtheXMLaboveandsettherelevantprojectpropertiesasshownintheFigure11.3:
Figure11.3Themanifestfileandnamespacedefinition fortheFlexLibraryProject
WewilladdtheriabookprojecttotheFlexBuildPathofourapplicationprojectas“SWCfolder”. Now we can move the DataGrid from our application project to theriabook and replace xmlns: fx=”com.theriabook.controls”withxmlns:fx=”http://www.theriabook.com/2006”,providedthatthe FlexBuildPathofourapplicationprojectincludesareferencetotheriabook.swc.Asaresult,our applicationwillreferencefx:DataGridandfx:DataGridColumn,irrespectiveoftheirphysicallocation. Havingdonethat,let’sgetbacktocustomizingtheDataGridColumn.
448
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
MoreonCustomizingtheDataGridColumn We’llputourDataGridColumninthesubfolderdataGridClassesasasignofourrespecttothewellthought-outdirectorystructureoftheFlexframework: // DataGridColumn.as (first version) package com.theriabook.controls.dataGridClasses { import mx.controls.dataGridClasses.DataGridColumn;
}
public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn { public function set formatString( fs:String ) : void{ FormattingManager.setFormat (this, fs); } }
Listing11.3DataGridColumn.as,thefirstversion Aswementioned,the“dirty”joboflocatingandassigningtheproperlabelfunctionhasbeendelegatedtothehelperclassFormattingManager.Thisclass,presentedinListing11.4,shouldbeput intoourtheriabookproject: // FormattingManager.as, first version package com.theriabook.controls.dataGridClasses { public class FormattingManager { import mx.controls.dataGridClasses.DataGridColumn; import mx.formatters.SwitchSymbolFormatter; private static var sf:SwitchSymbolFormatter; public static function setFormat(dgc:mx.controls.dataGridClasses.DataGridColumn formatString:String):void { switch (formatString.toLowerCase()) { case “ssn”: dgc.labelFunction = ssnLabelFunction; case “phone”: dgc.labelFunction = phoneLabelFunction; } } private static function ssnLabelFunction(item:Object, column: mx.controls.dataGridClasses.DataGridColumn): String { RIAWITHADOBEFLEXANDJAVA
449
CHAPTER11
tring {
}
if (!sf) { sf = new SwitchSymbolFormatter(); } return sf.formatValue(“###-##-####”, item[“SS_NUMBER”]); } private static function phoneLabelFunction(item:Object, column:DataGridColumn):S if (!sf) { sf = new SwitchSymbolFormatter(); } return sf.formatValue(“(###)###-####”, item[column.dataField]); }
}
Listing11.4FormattingManager.as,thefirstversion Tada!Andthewinneris…thedeveloper.Onceweaddtheriabook.swc(withDataGrid,DataGridColumn,andFormattingManager)tothelibrarypath,theapplicationcodegetsreducedtothefollowing:
Listing11.5ASimpleapplicationillustratingFormattingManager.as
ImprovingFormattingManager Inthepreviousexampleswe’veusedtheSwitchSymbolFormatterforbothphoneandssnformat-
450
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
ting.Assoonaswestartformatingnumbersorcurrencyvalues,it’snaturaltouseNumberFormatterorCurrencyFormatter–descendantsofmx.formatters.Formatter.Infact,Flexoffersadedicated formatterevenforthephoneformatting. WhileSwitchSymbolFormatterderivesfromObject,alltherestoftheformattersdescendfromFormatter.ByencapsulatingthisspecificSwitchSymbolFormatterinthecustomclassMaskFormatter, we’llhelpourselvestobasethenextversionofFormattingManagerentirelyonFormatters: //MaskFormatter.as package com.theriabook.formatters { import mx.formatters.Formatter; import mx.formatters.SwitchSymbolFormatter;
}
public class MaskFormatter extends Formatter { private var formatString:String; private var sf:SwitchSymbolFormatter; public function MaskFormatter( fs:String) { formatString = fs; sf = new SwitchSymbolFormatter(); } public override function format(val:Object):String { return sf.formatValue( formatString, val); } }
Listing11.6MaskFormatter.as LookhowthisMaskFormatter1simplifiesourFormattingManager:wecanreplaceallprivatemethodswithananonymousfunction,asshowninListing11.7.Pleasenotethatthereferencetothe appropriateformatterispreservedwiththeclosure: //com.theriabook.controls.dataGridClasses.FormattingManager.as package com.theriabook.controls.dataGridClasses { public class FormattingManager { import mx.controls.dataGridClasses.DataGridColumn; import mx.formatters.*; import com.theriabook.formatters.MaskFormatter; public static function setFormat( RIAWITHADOBEFLEXANDJAVA
451
CHAPTER11
}
}
}
dgc:mx.controls.dataGridClasses.DataGridColumn, formatString:String):void { var formatter:Formatter = null; switch (formatString.toLowerCase()) { case “ssn”: formatter = new MaskFormatter(“###-##-####”); break; case “money”: formatter = new CurrencyFormatter(); CurrencyFormatter(formatter).precision=2; break; case “phone”: formatter = new PhoneFormatter(); break; case “shortdate”: formatter = new DateFormatter(); break; case “zip”: formatter = new ZipCodeFormatter(); break; } if (formatter) { dgc.labelFunction = function ( item:Object, dgc:mx.controls.dataGridClasses.DataGridColumn):String { return formatter.format(item[dgc.dataField]); } }
Listing11.7FormattingManager.as HereisthetestingapplicationFormatStringDemo,seeListing11.8:
Listing11.13ExtendedPropertiesDemo.mxml
Figure11.6CustomCheckBoxusedasa drop-inrendererwithextendedproperties
We should have mentioned the alternative run-of-the-mill approach with inline itemRenderer, Listing11.14:
460
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
Listing11.14UsingCheckBoxasaninlinerenderer Arguably,theextendedPropertiesapproachismoreefficient,sinceitabsolvesMXMLofgenerating anextranestedclass(mx:Component)foreachcolumnofthiskind4.We’veintroducedyoutoyet anotherwayofcustomizingaDataGridColumn,andwe’llcontinuebuildingontopofitinthefollowingsections.
NitpickingCheckBox TherearesomeadditionalremarksthatweoughttomakeaboutourCheckBoximplementationat thispoint. ThefirstoneisrelatedtothehorizontalalignmentoftheCheckBox.Instincttellsusthatalabelfreecheckboxshouldbecenteredinthecolumnratherthanstuckintheleft-mostposition.Atfirst, youmaytryapplyingatextAlignstyletotheDataGridColumn–tonoavail.Thenyoumightresort toanotherrun-of-the-millapproachtocenterthecheckboxbyputtingitinsideacontainersuch asanHBox.Here’stheperformance-basedadviceendorsedbyFlexFrameworkengineers:avoid containersinsidetheDataGridcellatallreasonablecost.Inparticular,insteadofusingHBox,why notsubclasstheCheckBoxandoverridetheupdateDisplayList()method?Itgetsquitenatural,once you’vesteppedonthispath,sowe’lladdthecodeshownbelowtoourCheckBox(thecomplete codeforthecom.theriabook.controls.CheckBoxispresentedinListing11.15): import mx.core.mx_internal; use namespace mx_internal; . . . . override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if (currentIcon) { RIAWITHADOBEFLEXANDJAVA
461
CHAPTER11
}
}
var style:String = getStyle(“textAlign”); if ((!label) && (style==”center”) ) { currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2; }
Notetheuseofnamespacemx_internal.It’srequiredtoreferencethecurrentIconthatvisualizes thecheckbox,sincecurrentIcon–thechildoftheoriginalCheckBox–isoriginallyscopedasmx_ internal. NowwemodifythetestingapplicationtoincludetextAlign=”center”:
And,whenwerunit,allcheckboxesareintheirproperplace:
Figure11.7CenteredCheckBoxDemoscreenshot
Thesecondnitpickingpointisrelatedtoundefinedasapossiblevalueofaproperty.Underourcurrentbusinessscenario,wecanassumethatsomeoftheemployeesarenoteligibleforthedaycare benefit,andrelevantitemsinthedataProvider’scollectionarelackingtheBENE_DAY_CAREproperty,whichfordynamicitemscanbeexpressedasitem.BENE_DAY_CARE==”undefined.”
462
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
Does it make sense to show checkboxes for non-eligible employees? Perhaps, it doesn’t. In this casewe’dmakecurrentIconinvisible.Youcanselectadifferentapproachandshowafuzzycheckboximageinstead,butthisisbeyondthepoint5.ThefollowingmodificationofupdateDisplayList() doesthejobofremovingcheckBoxwhenthevalueisundefined: override protected function updateDisplayList(unscaledWidth:Number, {
}
unscaledHeight:Number):void super.updateDisplayList(unscaledWidth, unscaledHeight); if (currentIcon) { var style:String = getStyle(“textAlign”); if ((!label) && (style==”center”) ) { currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2; } currentIcon.visible = (_value!=undefined); }
ToaccommodatethischangewehavetoloosenuptheclassdefinitionsforvalueasshowninListing11.15,wherewechangeObjecttoundefined. ThenextandprobablythemostimportantfixisthatourCheckBoxeshavebeensilenced.Tryto clickonone,scrolltherowoutofviewandscrollitbackin.Thecheckboxdoesn’tretainyourselectionanditshouldn’t;we’venevercommunicatedthechangetotheunderlyingdata.Toremedy thesituationwe’lladdtheconstructormethod,wherewe’dstartlisteningonthe“change”event; oncetheeventisinterceptedwe’llmodifythedataitemwiththeCheckBoxvalue.That,inturn,will resultineitheronValueoroffValue,asperourvaluegetter: public function CheckBox() { super(); addEventListener(Event.CHANGE, function(event:Event):void{ if (listData && listData is DataGridListData ) { data[DataGridListData(listData).dataField] = value; } } ); }
Andthelastpoint:Flexcollectionsbythemselvesdonotnoticeanychangesdonetotheunderlying
RIAWITHADOBEFLEXANDJAVA
463
CHAPTER11
dataitems;itistheapplication’sresponsibilitytokeepthecollectionsinformed,especiallyifyouhave morethenoneviewbasedonthesamecollection.Inourcase,changeofthedatawouldgototally unnoticedbythecollectiondisplayedbyDataGrid,untilweexplicitlynotifyitwiththeCOLLECTION_ CHANGEevent.ThecompletecodeforthesecondversionofCheckBoxispresentedinListing11.15: //CheckBox.as package com.theriabook.controls { import flash.events.Event; import mx.controls.CheckBox; import mx.controls.dataGridClasses.DataGridListData; import mx.core.mx_internal; use namespace mx_internal; public class CheckBox extends mx.controls.CheckBox { public var onValue:Object=true; public var offValue:Object=false; private var _value:*; public function CheckBox() { super(); addEventListener(Event.CHANGE, function(event:Event):void{ if (listData && listData is DataGridListData) { data[DataGridListData(listData).dataField] = value; var evt:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_ CHANGE, false, false, CollectionEventKind.UPDATE, -1, -1, [data]); mx.controls.DataGrid(DataGridListData(listData).owner).dataProvider. dispatchEvent(evt); } } ); } public function set value(val:*) :void { _value = val; invalidateProperties(); } public function get value():Object { if (_value==undefined) return _value; else return selected?onValue:offValue; }
464
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid override protected function commitProperties():void { if (_value!=undefined) selected = (_value == onValue); super.commitProperties(); } override public function set data(item:Object):void { super.data = item; if( item!=null ) { value = item[DataGridListData(listData).dataField]; } } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
} }
}
super.updateDisplayList(unscaledWidth, unscaledHeight); if (currentIcon) { var style:String = getStyle(“textAlign”); if ((!label) && (style==”center”) ) { currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2; } currentIcon.visible = (_value!=undefined); }
Listing11.15CheckBox.as,thesecondversion Nextcomesthetestapplication.We’veaddedthe“Revokedaycarebenefit”button,whichturns DAY_CARE_BENEintoundefinedonthecurrentlyselectedDataGriditem.Wealsohadtonotifythe collectionwiththeitemUpdated()call:
Listing11.18UndefinedCheckBoxDemo.mxml Nowlet’stesttheitemRenderer/itemEditorscenario.Notthatit’srequired,butwewouldpreferto layouttheradiobuttonshorizontally.So,we’llcreateRadioButtonGroupHBoxasasimpleextensionofRadioButtonGroupHBox,takingcareofthebox’sdirectionandaddingacoupleofpadding pixels(thedefaultBoxpaddingis0):
474
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid // RadioButtonGroupHBox.as package com.theriabook.containers { public class RadioButtonGroupHBox extends RadioButtonGroupBox { public function RadioButtonGroupHBox() { super(); direction = “horizontal”; setStyle(“paddingLeft”, “5”); } } }
Listing11.19RadioButtonGroupHBox.as AscreenshotofthetestapplicationRadioButtonGroupHBoxDemoisinFigure11.10.Asyoumay notice in the corresponding Listing 11.20, we’ve declared the entire DataGrid editable and indicatedforthe“status”columnthatrendererIsEditor=”true”:
Figure11.10AsnapshotoftheRadioButtonGroupHBoxDemo
RIAWITHADOBEFLEXANDJAVA
477
CHAPTER11
Listing11.21StandardDynamicStyleDemo.mxml WhenyourunStandardDynamicStyleDemo,you’llseethepictureinFigure11.11:
478
Figure11.11AsnapshotoftheStandardDynamicStyleDemo
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
ComputedColumnBackground We can’t apply the same technique for background color because Label doesn’t support backgroundColor style.We can resort to TextInput, which does have backgroundColor, but missing somebackgroundColordoesn’tseemagoodreasonforgivingupalightweightLabelinfavorof TextInput.Afterall,thebeautyofFlexisthatframeworkcontrolsareopentoextension.So,hereit is,ourcustomLabelextendedwithbackgroundColorsupport,Listing11.22: // Label.as (theriabook.swc) package com.theriabook.controls { import mx.controls.Label; [Style(name=”backgroundAlpha”, type=”Number”, inherit=”no”)] [Style(name=”backgroundColor”, type=”uint”, format=”Color”, inherit=”no”)] dynamic public class Label extends mx.controls.Label { override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if (getStyle(“backgroundColor”)){ graphics.clear(); graphics.beginFill(getStyle(“backgroundColor”), getStyle(“backgroundAlpha”)); graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); graphics.endFill(); } } }
}
Listing11.22Label.as Asyoucansee,we’vedefinednotone,buttwostyles–backgroundAlphaandbackgroundColor –andweusebothvalueswith graphics.beginFill()insidetheoverriddenimplementationof updateDisplayList().OnceweaddLabel.astotheriabook.swcandregisteritinthecomponentmanifestXML,theDataGridColumncanberedefinedasinthefollowingsnippet:
RIAWITHADOBEFLEXANDJAVA
479
CHAPTER11
ThedifferencebetweentheapproachaboveandthewaywedefinedasimilarDataGridColumn in Listing 11.22 is that now we compute backgroundColor instead of the color and use fx:Label insteadoftheonefrommxnamespace.Ifyoudothereplacementandruntheprogram,you’llsee thepictureinFigure11.12.
Figure11.12Snapshotofthebackgroundcolorcomputed withbindingexpression Despitetheseeminglysatisfyingresult,there’ssomethingwronghere:usingthepowerfulitemrenderermechanismtomanagestylesseemstobedesignoverkill.Let’sspeakourminds:we’reafter dynamicruntimestyles,right?So,wouldn’titbeniceifweaddedanextraDataGridColumnattribute,called,say,runtimeStyles,wherewecouldlistallstylesandabstractfromtheimplementation.Belowisanexample:
Thisapproachwouldletdevelopersconcentrateonthesubstanceratherthanontheprocess.Let’s makeithappen.
480
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
RuntimeColumnStylesUnleashed Here’stheplan.We’llupgradethedefaultitemRendererofthefx:DataGridfromUITextFieldtoour customfx:Label.We’llextendfx:DataGridColumnwithanextraproperty–runtimeStyles.Finally, we’ll intercept data changes to anitem renderer,whetherdefaultornot,toreassignallruntime Stylesoneach. Here’showtheconstructorofthestandardDataGridassignsitemRenderer: package mx.controls { public class DataGrid extends DataGridBase implements IIMESupport { public function DataGrid() { super(); itemRenderer = new ClassFactory(DataGridItemRenderer); . . . . . . } } }
WehavetofightthetemptationtoreplacetheassignmentoftheitemRendererwith itemRenderer = new ClassFactory(com.theriabook.controls.Label);
Hereiswhy.AClassFactoryinstanceisa“factoryobject,”whichisusedtogenerateinstancesofanotherclass(akaageneratorclass),withthenewInstance()method.Accordingtoourplanweneed tointerceptdatachangestoanyinstanceofthegeneratorclassitemrenderer.Precisely,we’llhave tolistentotheFlexEvent.DATA_CHANGEeventoneveryinstanceofthecom.theriabook.controls. Labelcreatedby“thefactory.”Hmm,whatcouldbesimplerthanaddingtheneededeventlistener tothecontrols?Thatwouldbeokayifwecommitourselvestofx:Labelastheonlyitemrenderer andbynomeansdoweproposetotakethepowerofcustomitemrenderersaway.Tomakethe runtimeStylescontrolmechanismagnostictothetypeofrenderer,we’dliketolistentoFlexEvent. DATA_CHANGEontheinstancesofanygeneratorclass. Itonlysoundsdifficult.Afterall,aClassFactoryisnothingbutanimplementationoftheIFactory interfacewithasingleproperty–propertiesandsinglemethod–newInstance().That’sit.Sowecan easilywrapastandardClassFactoryinsideourcustomonetointerceptthenewInstance()call.We’ll callourwrappingclassfactorytheUIClassFactory: function DataGrid() { super(); itemRenderer = new UIClassFactory(ClassFactory(com.theriabook.controls.Label)); } RIAWITHADOBEFLEXANDJAVA
481
CHAPTER11
The constructor of the UIClassFactory would simply store the reference to the instance of the real class factory – cf. Meanwhile UIClassFactory’s newInstance() would delegate the call to the cf.newInstance().ItwouldalsoregisterthelistenertotheFlexEvent.DATA_CHANGEeventasshown below: public class UIFactory implements IFactory { . . . . . public function UIClassFactory( cf:ClassFactory ) { wrappedClassFactory = cf; } public function newInstance():* { var obj:* = wrappedClassFactory.newInstance(); obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange); return obj; }
}
private function onDataChange(event:FlexEvent):void{ . . . . . }
Asareminder,propertiesoffactory-manufacturedobjectsgetassignedbyiteratingover…fasten yourseatbeltsplease…propertiesofthespecificpropertyofthefactoryobject.Thenameofthis aggregatingpropertyisproperties.Thatwayallinstancesareinitializedwiththesamevalues.Wrappingthepropertiespropertyisquitesimple: public function set properties(v:Object):void { wrappedClassFactory.properties = v; } public function get properties():* { return wrappedClassFactory.properties ; }
ThecompletecodeofUIClassFactoryispresentedinListing11.23.Theonlypartofitthatremains uncoveredistheonDataChange()handler. The implementation of the default DataGridUtemRenderer emits a DATA_CHANGE event from twoplaces:setterofdataandsetteroflistData.WewillignorechangesoflistDataand,forevery datachange,wewillreassignallstylenamepropertiescarriedbyruntimeStyles.Thevaluesofthese propertiesmaybeliteralor,alternatively,functionreferences.Inthelattercase,weapplythecall operator()beforesettingthestylevaluewiththesetStyle():
482
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid //UIClassFactory.as package com.theriabook.util { import mx.core.IFactory; import mx.core.ClassFactory; import mx.events.FlexEvent; import mx.controls.dataGridClasses.DataGridColumn; public class UIClassFactory implements IFactory { private var wrappedClassFactory : ClassFactory; public function set properties(v:Object):void { wrappedClassFactory.properties = v; } public function get properties():* { return wrappedClassFactory.properties ; } public function UIClassFactory( cf:ClassFactory ) { wrappedClassFactory = cf; } public function newInstance():* { var obj:* = wrappedClassFactory.newInstance(); obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange); return obj; } private function onDataChange(event:FlexEvent):void{ var renderer:Object = event.currentTarget; // In the default DataGridItemRenderer both data and listData are // Bindable(“dataChange”)] // We want to skip assinments to listData if (renderer.data is mx.controls.dataGridClasses.DataGridColumn) return; // Act only on ‘dynamic style’ columns if (renderer.styleName && renderer.styleName.hasOwnProperty(“runtimeStyles”)) { var runtimeStyles:Object = renderer.styleName[“runtimeStyles”]; for (var style:String in runtimeStyles) { if ( runtimeStyles[style] is Function ) { var functionObject : Function = runtimeStyles[style]; renderer.setStyle(style, functionObject(renderer.data)); RIAWITHADOBEFLEXANDJAVA
483
CHAPTER11 } else renderer.setStyle(style, runtimeStyles[style]);
}
}
}
} renderer.invalidateDisplayList();
}
Listing11.23UIClassFactory.as,firstversion Onechoreisremaining.AslongaswewanttocommunicatetheruntimeStylestoanyitemrenderer,includingtheonesthatareindividuallysetonaper-columnbasis,weneedtoroutethem through our UIClassFactory. Accordingly we will modify our DataGridColumn and override the implementationoftheDataGridColumn’sitemRenderersetter: override public function set itemRenderer( val : IFactory ) : void { super.itemRenderer = new UIClassFactory(val as ClassFactory); }
ThefulllistingofourDataGridColumnisshowninListing11.24 // DataGridColumn.as package com.theriabook.controls.dataGridClasses{ import com.theriabook.util.UIClassFactory; import mx.controls.dataGridClasses.DataGridColumn; import mx.core.ClassFactory; import mx.core.IFactory; public class DataGridColumn
extends mx.controls.dataGridClasses.DataGridColumn {
public var runtimeStyles:Object = null; public var runtimeProperties:Object = null; public function set extendedProperties(val:Object) :void { if (itemRenderer) itemRenderer[“properties”] = val; } override public function set itemRenderer( val : IFactory ) : void { super.itemRenderer = new UIClassFactory(val as ClassFactory); }
484
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid public function set formatString( fs :String ) : void{ formatData = fs; }
}
}
public function set formatData( fd :Object ) : void{ FormattingManager.setFormat(this, fd); }
Listing11.24DataGridColumn.as Hereisourtestapplication–RuntimeStyleDemo,Listing11.25:
RIAWITHADOBEFLEXANDJAVA
491
CHAPTER11
Listing11.27NumericInputDemo.mxml //NumberScope.as, helper class for NumericInputDemo.mxml package { public class NumberScope { public var number:*; public function get description():String { if (number===null) return ‘null’; if (number===undefined) return ‘undefined’; return (typeof number); } public function NumberScope(n:*) { number=n; } } }
Listing11.28NumberScope.as,helperclassforNumericInputDemo Finally,Listing11.29depictsthecompletecodeforNumericInput: // NumericInput.as package com.theriabook.controls { import mx.controls.TextInput; import flash.events.Event; import mx.controls.List; public class NumericInput extends TextInput
492
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid {
import mx.controls.listClasses.BaseListData; import mx.events.FlexEvent; import mx.controls.dataGridClasses.DataGridListData; public function NumericInput() { super(); addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput); } private var _value:*; [Bindable(“change”)] public function set value(v:*):void { _value = v; if ((String(v)==”NaN”) || (v==null /*or undefined*/)) { text=””; } else { text = String(v as Number); } } public function get value():* { // Preserve NaN - null - undefined, when nothing has been entered if (((_value!=null )&& (String(_value)!=”NaN”)) || (text!=””) ) { _value = Number(text.replace(/,/g,””)); // deformat first } return _value; } public override function set data(item:Object):void { if (listData && listData is DataGridListData) { var dgListData:DataGridListData = DataGridListData(listData); value = item[dgListData.dataField]; dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); return; } super.data = item; } private function onTextInput(event:flash.events.TextEvent):void { // TODO Find out number separators from the locale settings var re:RegExp = new RegExp(“[^0-9,.-]”, “i”); var illegalCharacterFound:Boolean = re.test(event.text); RIAWITHADOBEFLEXANDJAVA
493
CHAPTER11
}
}
}
if (illegalCharacterFound) { event.preventDefault(); }
Listing11.29NumericInput.as,finalversion
DataGridwithAutomaticItemEditors ArmedwithMaskedInputandNumericInput,wecanproceedwithautomatingDataGridediting features.Here’stheissueathand:onceyoumakeyourDataGridaseditable,youareinchargeof manuallyassigningtheappropriateitemeditorsforeachcolumn.Comparethistothestandard formattingapproach,whichassumesthatforeachcolumn,youmanuallyassignanitemrenderer orlabelFunction.Doyouseethe100%similarity?Makenomistakewe’renotcomparingitemeditorswithlabelfunction.It’sthe“manually”we’reafter. Asthereaderisprobablysensingbynow,weareabouttoautomatetheprogrammingofeditors withthesametechniqueweappliedforformatting.Namely,we’lltaketheburdenoffdevelopers’ shouldersandmakeDataGridaccountableforpickingupandinitializingproperitemeditors. Ifyourecall,mappinglabelfunctions,albeitanonymousones,hadbeendelegatedtoaseparate class,FormattingManager.Quitesimilarly,weareabouttointroduceanadditionalclassEditingManager,withasinglemethodsetFormat()sothatourcustomDataGridColumnwillbecallingit rightafteritinvokesthesimilarmethodoftheFormattingManager: // DataGridColumn.as package com.theriabook.controls.dataGridClasses{ import com.theriabook.util.UIClassFactory; import mx.controls.dataGridClasses.DataGridColumn; import mx.core.ClassFactory; import mx.core.IFactory; public class DataGridColumn
extends mx.controls.dataGridClasses.DataGridColumn {
public var runtimeStyles:Object = null; public var runtimeProperties:Object = null; public function set extendedProperties(val:Object) :void { if (itemRenderer) itemRenderer[“properties”] = val; } override public function set itemRenderer( val : IFactory ) : void {
494
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
}
super.itemRenderer = new UIClassFactory(val as ClassFactory);
public function set formatString( fs :String ) : void{ formatData = fs; }
}
}
public function set formatData( fd :Object ) : void{ FormattingManager.setFormat(this, fd); EditingManager.setFormat(this, fd); }
Listing11.30DataGridColumn.as,improvedwithEditingManager Nowlet’sfocusonEditingManager. ItsmethodsetFormatedhastoassignthevalueoftheitemEditorpropertyforthecolumn.ThedecisionofwhichitemEditortochooseshouldbebasedentirelyontheformatString.Asyoucansee,the setFormat()methodcarriesalltherequiredinformationinitssignature,andcolumnandformatting informationasmethodparameters.Beforewegocoding,let’srecapthatitemEditorisapropertyof type IFactory.When you set its value in MXML, the compiler automatically creates an instance of ClassFactory,aclassthatimplementstheIFactoryinterfacebasedonyourclassas“generator.”Our approachisdifferentandwe’llbecreatingtheClassFactory12explicitly. Here’s an example of creating the ClassFactory for the column with the“shortdate” format.We createtheClassFactorybasedontheDateFieldandsetitspropertieswithananonymousobject sothat,ultimately,eachinstanceofthemanufacturedDateFieldwillhaveitsformatStringsetto “MM/DD/YY.”NotonlydoweassigntheitemEditortotheDataGridColumn,wealsospecifythe editorDataField(DataGridassumesthetextpropertyotherwise): case “shortdate”: cf = new ClassFactory(DateField); cf.properties = { formatString : “MM/DD/YY”}; dgc.editorDataField=”selectedDate”; dgc.itemEditor = cf; break;
Likethe“zip,”“phone,”and“ssn”formats,allofwhichconformnicelytoMaskedInput,weencapsulatetheassignmentofitemEditorinsidethehelperfunctionsetMaskedEditor(): void{
private static function setMaskedEditor(dgc:DataGridColumn, mask:String):
RIAWITHADOBEFLEXANDJAVA
495
CHAPTER11
}
var cf:ClassFactory; cf = new ClassFactory(MaskedInput); cf.properties = {inputMask : mask}; dgc.itemEditor = cf;
ThefullcodeforEditingManagerispresentedinListing11.31.Notethat,likeFormattingManager, weprogrammedthedefaultcasetohandlethenon-listedformatStringwitha“#”characterinitas anunknownmask: // EditingManager.as package com.theriabook.controls.dataGridClasses { import mx.core.ClassFactory; import mx.controls.TextInput; import com.theriabook.controls.MaskedInput; import com.theriabook.controls.NumericInput; import mx.controls.DateField; public class EditingManager { import com.theriabook.controls.dataGridClasses.DataGridColumn; import mx.formatters.*; import com.theriabook.formatters.MaskFormatter; private static function setMaskedEditor(dgc:DataGridColumn, mask:String):void{ var cf:ClassFactory; cf = new ClassFactory(MaskedInput); cf.properties = {inputMask : mask}; dgc.itemEditor = cf; } public static function setFormat(dgc:DataGridColumn, formatData:Object):void{ var fs:String; var cf:ClassFactory; if (formatData is String) fs = formatData as String; else fs = formatData.formatString; switch (fs.toLowerCase()) { case “ssn”: setMaskedEditor(dgc, “###-##-####”); break; case “money”:
496
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
}
}
}
}
cf = new ClassFactory(NumericInput); dgc.itemEditor = cf; break; case “shortdate”: cf = new ClassFactory(DateField); cf.properties = { formatString : “MM/DD/YY”}; dgc.editorDataField=”selectedDate”; dgc.itemEditor = cf; break; case “zip”: setMaskedEditor(dgc, “#####-####”); break; case “phone”: setMaskedEditor(dgc, “(###) ###-####”); break; default: if (fs.indexOf(“#”)!=-1) { setMaskedEditor(dgc, fs); };
Listing11.31EditingManager.as Finally,Listing11.32presentsthetestapplication–ComputedEditorDemo:
Listing11.34DataGridAutomationDemo.mxml Pleasenoticetwoarrays:cols,withmetadataforDataGridcolumns,anddp,withthedataProviderto-bedata.Botharraysgethard-codedinsidetheonCreationComplete()method,butyoucaneasilyimaginehowtheygetloaded,say,fromadatabase. Right above the onCreationComplete() there are commented-out imports of the RadioButtonGroupHBoxandthelinkagevariabledeclaration.Theselines,iflefttocompile,couldabsolveus fromturningthetheriabook.swcintoaself-initializedlibrary.But,howcanweknowinadvance whichclassdefinitionsarerequiredbythecolumn’smetadataifitgetsloadedfromthedatabase? Wecan’tand,again,werecommendmakinglibrariesself-sufficientasa“ruleofthumb.” Thelastcommenttotheapplicationconcernsthewaywe’veassignedDataGridColumnproper-
502
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
ties:weclearlysidedoutitemRenderertomakethecorrespondingUIClassFactory(hadwewanted to,asimilarroutewouldhavebeentakenforitemEditor): var dgc:DataGridColumn= new DataGridColumn(); for (var prop:String in cols[i]) { if (prop==”itemRenderer”) { dgc.itemRenderer = new UIClassFactory(cols[i][prop]); dgc.rendererIsEditor=true; } else { dgc[prop] = cols[i][prop]; } }
Figure11.16AsnapshotoftheDataGridAutomationDemoapplication
Figure11.14depictstheDataGridAutomationrunning14.Wehopeitsparksthereader’sinterestin dynamicapplications.Whatcouldbenexthere?Well,thestructureofthemetadatacols,resembles one-to-onetheMXMLsectionoftheDataGridColumndefinitions.So,wecouldpossiblystorethe MXMLinthedatabaseandinterpretitwithActionScriptasE4X.Potentially,wecouldevenallow interactiveeditingofsuchMXML,makingitaself-modifyingprogram.Butthiswouldtakeusway beyondtheboundariesofthischapter15.
PivotedDataGridorPropertyBag TocompleteourjourneyintoDataGridautomationlandwe’llshowhowDataGridcanbeappliedin averypopularusecase:ascrollablepropertybag.Let’slookatFigure11.15.HowmanyDataGrids doyouthinkarethere?Theansweristwo.TheleftpartofthescreenisnotaFormbutaDataGrid. Wetookofftheverticalgridlines,removedtheheaders,andhadthealternatelinecolorsmatchthe RIAWITHADOBEFLEXANDJAVA
503
CHAPTER11
backgroundoftheapplication,butunderneaththisveneerit’sstillagoodoldDataGrid.
. . .
BothDataGridssharethedataProvider–theArrayCollectionbasedonanarrayofColumnRecord types: //ColumnRecord.as package { public class ColumnRecord { public var columnLabel:String; public var columnType:String; public var columnValue:*; public var expando:Object;
}
}
public function ColumnRecord(l:String, t:String, v:*, x:Object=null) { columnLabel=l; columnType=t; columnValue=v; expando=x; }
Listing11.35ColumnRecord.as,usedbyPropertyBagDemo
504
RIAWITHADOBEFLEXANDJAVA
AdvancedDataGrid
Figure11.17ThePropertyBagDemoapplication
Thecompletecodefortheapplication–PropertyBagDemo–isinListing11.36:
[Flex] false true false true
Endpoint.* Service.* RIAWITHADOBEFLEXANDJAVA
521
CHAPTER12 Configuration
Thelogging-levelattributecanhaveoneofthefollowingvalues:all,debug,info,warn,error,or none. Youcanalsocontroltheoutputoftheserver-sideMXMLcompiler.Youcanlogmessagessentto theWebapplicationserver’sloggingmechanismbyusingthelog-compiler-errorspropertyinthe /WEB-INF/flex/flex-webtier-config.xmlfile.TheWebapplicationloggerdoesnotlogruntimemessages,butonlylogsserver-sidecompilermessages. Tologcompilererrorstoafileorconsole,youmustsetthevalueofthetagto falseandtotrue. false …
true
The location, number of backups and log file sizes are block of the flex-webtier-config.xml file.
specified in the logging
info true
true/WEB-INF/flex/logs/flex.log 200KB 3
Whenswitchingtoproductionmode,alwaysswitchtheloggingleveltoinfoorerrorsaslogging tendstobeexpensivesinceitaccessesthefilesystemandperformsdataserialization.Don’tforget thatverificationofthelogginglevelsinthecodeistarget-orientedversusatypicalJavapackage/ class granularity, and this presents additional performance challenges.The same applies to the loggingorganizationontheclient.Wewillillustratethisindetailinthenextsection.
Client-SideLogging TherearetwocommonlyusedtechniquesforloggingtheFlexprograms.First,thedebuggingversion oftheplayerprovidesabuilt-inglobalfunctiontrace().Itsoutputgoestothefileflashlog.txtthat’s
522
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
locatedinthesamefolderasmm.cfgfile.Pleaserefertotheproductdocumentationtofindthename ofthefolderinyourOS(seehttp://livedocs.macromedia.com/flex/2/docs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001528.html). If you don’t file mm.cfg in thisdirectory,createonewiththefollowingcontent: ErrorReportingEnable=1 TraceOutputFileEnable=1 TraceOutputFileName=c:\flexLog.txt MaxWarnings=50. Inmm.cfg,youmustsetTraceOutputFileEnableto1,andyoucansetacustomvalueforTraceOutputFileName.Moreimportant,trace()automaticallygoestotheconsolepaneloftheFlexBuilder.It’s agoodwaytostarttracing,similartoSystem.out.printlninJava.Theonlydifferenceisthatthelatter worksinbothdebugandruntimemodes,whilethefunctiontrace()worksonlyinthedebugmode. ThesecondapproachistousetheLoggingAPI,whichallowsdeveloperstocontrolloggingbased ontwoparameters–levelsandcategories–inamoreconvenientway.Italsoallowsdevelopersto createanddesignatetargets–classesresponsibleforwritingtheloggingmessagestosomeoutput media,whichcanbelocalresources,serverlogs/messaging,orevenaremoteworkstationusedfor debugging/support. Let’stakeamoredetailedlookattheFlexloggingAPI.
UsingtheLoggingAPI Tostartloggingmessagesyouneedtwoobjects:aloggerandalogtarget.Theclassmx.logging.Log outoftheboxprovidesbasicloggingfunctionalitywithapre-builtimplementationofthelogger interface ILogger with error(), info(), warn(), and debug() methods. Application developers just needtodefinetheloggerandcallthemessagingmethodsonitsbehalf:
import mx.logging.Log; import mx.logging.ILogger; private static var logger:ILogger=Log.getLogger(“MyCategory.subcotegory.moredetails”);
Inthecodesnippetabove,MyCategoryisjustalogicalnamethatisnotlinkedtoanyrealclassname. Thiscodeisquitegenericanddoesnotspecifywheretooutputmessages.Flexincludestwostandard targets:TraceTargetandMiniDebugTarget.TraceTargetusestracetowriteoutthemessagesandrequiresyoutouseadebuggingversionoftheplayerandtodothetracesetupdescribedearlier.MiniDeRIAWITHADOBEFLEXANDJAVA
523
CHAPTER12
bugTargetusesLocalConnectiontotransfermessagestootherFlash/Flexapplications.HereisasampleofsettingTraceTargetwithsomeformattingtoincludetimeandlevelalongwiththemessage:
import mx.logging.targets.TraceTarget; import mx.logging.Log; import mx.logging.ILogger; private static var logger:ILogger = Log.getLogger(“MyCategory”); private function setTarget() : void { var target : TraceTarget = new TraceTarget(); target.includeTime = target.includeLevel = true; Log.addTarget( target ); };
WhileTraceTargetisgoodforsomecases,it’simportantthatadeveloperhastheoptiontooutputto otherservicessuchasaserverlog,remoteworkstations,orOSconsoles.Let’screateothertargets.
ServerLogTarget Herewe’llcreateasimpletargettooutputmessagestotheserverusingLog4J,apopularJavatool. TheJavaportionimplementingloggingisverysmall: package com.theriabook.logging; import org.apache.log4j.Logger; import org.apache.log4j.Level; public class ServerFlexLogger{ static public void log(String levelStr, String message) { logger.log(Level.toLevel(levelStr), message); }
}
static Logger logger; static { logger = Logger.getLogger(“FlexLogger”); logger.setLevel(Level.ALL); }
Listing12.1ServerFlexLogger.java
524
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Once compiled, deployed, and registered with Flex Remoting, the server-side Java class ServerFlexLoggerisreadytobecalledfromtheclient-sideActionScriptclassServerTarget: package com.theriabook.util.logging { import mx.logging.LogEvent; import mx.rpc.remoting.RemoteObject; import mx.rpc.events.FaultEvent; import mx.logging.AbstractTarget; import mx.logging.LogEventLevel; // Outputs logs to the web server. public class ServerTarget extends AbstractTarget private var _destination: String; private var ro: RemoteObject;
{
public function ServerTarget(destination: String) { super(); this.destination=destination; } override public function logEvent(event:LogEvent):void { var message:String = event.message; var category:String = ILogger(event.target).category; var level:int= event.level; if ((category == “mx.messaging.Producer” || category == “mx.messaging.Channel” || category.substr(0,7) == “mx.rpc.” ) && level < LogEventLevel.ERROR) return; ro.log(LogEvent.getLevelString(level),message); } public function get destination(): String { return _destination; } public function set destination(val: String): void { _destination=val; ro = new RemoteObject(); ro.destination=_destination; ro.addEventListener(FaultEvent.FAULT,ro_onFault); } protected function ro_onFault(evt:FaultEvent):void { RIAWITHADOBEFLEXANDJAVA
525
CHAPTER12
}
}
}
trace(evt.fault.faultString+” --- “+evt.message.destination);
Listing12.2ServerTarget.as Wehadtolimittheloggingofthemessagesfromthemx.messagingandmx.rpcpackagestoavoid gettingintoanendlessloop.Thereasonisthatmx.messagingandmx.rpcpackagescontaintheir ownloggingmessagesaboutinternalcommunicationprocessesthatallowapplicationdevelopers todebugtheflowofservercommunications,andthesemessagescanpileupprettyfast. Finally,let’screateaFlexapplicationthatregisterstheServerTarget:
import mx.logging.Log; import mx.logging.ILogger; private static var logger:ILogger = Log.getLogger(“MyCategory”); private function setTarget() : void { var target : ServerTarget = new ServerTarget(“logging”); Log.addTarget( target ); };
Listing12.3ASampleFlexApplicationwithLogging
ServerLogTargetUsingPlainHTTP Listing12.4willusearawsocketcommunicationontheclientandaservletontheservertoremove thetrapoffallingintothecyclicloggingofmx.rpcandmx.messaging. package { import mx.logging.AbstractTarget; import flash.net.URLRequest; import flash.net.sendToURL; import mx.logging.LogEvent; import mx.logging.ILogger; import flash.net.URLVariables; public class SocketTarget extends AbstractTarget
526
RIAWITHADOBEFLEXANDJAVA
{
LoggingandDebuggingFlexandJavaApplications public function SocketTarget(destination: String) { super(); this.destination=destination; } private var _destination: String; private var req: URLRequest;
}
}
override public function logEvent(event:LogEvent):void { var msg : URLVariables = new URLVariables(); msg.level=LogEvent.getLevelString(event.level); msg.message=event.message; msg.category = ILogger (event.target).category; req.data= msg; sendToURL( req); } public function get destination(): String { return _destination; } public function set destination(val: String): void { _destination=val; req = new URLRequest(val); req.method = «POST»; }
Listing12.4SocketTarget.as IntheapplicationobjectweneedtoinstantiateSocketTargetwiththeURL“logging”.
import mx.logging.Log; import mx.logging.ILogger; private static var logger:ILogger = Log.getLogger(“MyCategory”); private function setTarget() : void { var target : SocketTarget = new SocketTarget(“logging”); Log.addTarget( target ); };
RIAWITHADOBEFLEXANDJAVA
527
CHAPTER12
Listing12.5LoggingwithSocketTarget WewillalsoneedtoconverttheJavaclassfromListing12.1intoaservlet: package com.theriabook.logging; import org.apache.log4j.Logger; import org.apache.log4j.Level; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FlexLoggerServlet extends HttpServlet { protected void doPost(final HttpServletRequest request, final HttpServletResponse response) { final String levelStr = request.getParameter(“level”); final String message = request.getParameter(“message”); logger.log(Level.toLevel(levelStr), message); }
}
static Logger logger; static { logger = Logger.getLogger(“MyFlexLogger”); logger.setLevel(Level.ALL); }
Listing12.6FlexLoggerServlet.java Youmightwanttoincludetheclient’sIPaddressandtheuserinformationintheloginorderto distinguishtheoutputfrommultipleclients.
TheClient-SideTarget Finally,we’llcreateatargettocommunicatelogmessagestoastandaloneFlashapplicationusing theclassLocalConnection.Listing12.7providestheimplementationoftheLocalConnectionTargetclass: package { import mx.logging.LogEvent; import mx.logging.AbstractTarget;
528
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications import mx.logging.Log; import flash.events.StatusEvent; import flash.net.LocalConnection; import mx.controls.Alert; import mx.logging.LogEventLevel; import mx.logging.ILogger; public class LocalConnectionTarget extends AbstractTarget { private var _destination: String; private var preventSubsequentErrors: Boolean = false; private var conn:LocalConnection; public function LocalConnectionTarget (destination: String) { super(); this.destination=destination; } override public function logEvent(event:LogEvent):void { var level:String = LogEvent.getLevelString(event.level); var message:String = event.message; var category : String= ILogger (event.target).category; conn.send(destination, «lcHandler», message,category,level,new Date()); } private function onStatus(event:StatusEvent):void { switch (event.level) { case «error»: if (!preventSubsequentErrors){ Alert.show(«LocalConnection.send() failed\n»+ «destination: «+destination); } preventSubsequentErrors =true; }
} public function get destination(): String { return _destination; }
public function set destination(val: String): void { _destination=val; preventSubsequentErrors=false; conn = new LocalConnection(); conn.addEventListener(StatusEvent.STATUS, onStatus); }
RIAWITHADOBEFLEXANDJAVA
529
CHAPTER12 public function releaseOutput ():void {
}
}
}
Listing12.7LocalConnectionTarget.as TheclassLocalConnectionTargetcanbeusedtodeliverloggingmessagestoanyclientapplication thateitherembedsaFlashcomponentorimplementsLocalConnectionwithanativeimplementationusinglow-levelprogramminglanguages.Thecodethatcomeswiththebookincludesafree nativeimplementationfortheWindowsplatformofaconsoleapplicationthatallowsyoutooutput thelogtothedisplaymonitor,thefiles,andtheWindowsnativedebugginglog(seehttp://www. microsoft.com/technet/sysinternals/Miscellaneous/DebugView.mspx).
CreatingaTracingConsoleforLocalConnection Tooutputtheloggingmessages,we’llneedsomedisplaypanels.We’vecreatedasimpleDataGridbased component LogsPanel that will display new log messages upon their arrival (see Figure 12.1).
Figure12.1Displayinglocallogmessagesusinglogspanel
Listing12.8providessamplecodetodirectthelogmessagestoourpanel:
530
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Listing12.8ASampleApplicationthatLogstoLogsPanel TheLoggingGridcomponent(seeListing12.8)isinheritedfromtheDataGrid:
RIAWITHADOBEFLEXANDJAVA
531
CHAPTER12
TheRIAAspectofFlexLogging Thedevelopermightwanttochangethesettingsoftheloggingwhiledebuggingtheapplication. Duringthelifespanoftheapplication,youmayneedtobeabletoredirectthelogmessagestoadifferenttarget(s).SuchfunctionalitycanbeimplementedbydevelopingaLoggingManager,whichis aUIcomponentthatallowsinteractivecontrollevels,categories,andtargets. Aloggingmanagershouldsupportthefollowingfunctionalityontheclientside: • • •
Automaticallydiscoverandlistloggingcategories,andallowchangeinthelogginglevelfor eachcategoryorpackage. Listregisteredtargets/destinations,andsetadefaultlogginglevelandothercommonparameters. Providetheclient-sidepersistenceofloggingsettings.
Figure12.2showsasampleloggerpanelthatmeetsalloftheaboverequirements.
532
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Figure12.2Asampleloggingmanager
Figure12.3showsanextensiontoourLoggingGridcomponentthatprovidesclient-sidefiltering, searching,andothercustomloggingfunctions.
Figure12.3AsampleextensionofaLoggingGridcomponent
RIAWITHADOBEFLEXANDJAVA
533
CHAPTER12
Thereareanumberoffreewareandcommercialcomponentsthatallowyoutodirectandshow loggingdatainEclipse,aWebbrowser,andastandaloneapplication.Youcanfindafreeopen source tracing console called XPanel at the following URL: http://www.faratasystems.com/ ?page_id=45.
Debugging Inthissectionwe’llgothroughsomescenariosofdebuggingJavaserver-sidecodedeployedinone oftheJavaEEapplicationservers.We’lldiscusstwomodes: • RemoteDebugging:WhenyoucanconnecttoarunningapplicationserverthroughadedicatedportanddebugyourJavaapplication • InternalDebugging:WhenyoustartyourapplicationserverinsidetheEclipseIDEandthen debugthecodeaccordingtosetbreakpoints
RemoteDebugging We’llillustrateremotedebugging,assumingthatyou’veinstalledFlexDataServiceswiththeintegratedJRunserver,eventhoughyoucanapplythesametechniqueswithotherJavaEEapplication servers.TheplanistoengagetheJavaPlatformDebuggerArchitecture(JPDA),whichallowsyouto debugapplications(JRuninourcase)runninginaJVM.Besidesrunningtheserver,JVMwillalso listentoaparticularport,andtheFlexapplicationthatwe’dliketodebugwillattachtothisportfor debuggingtheserver-sideJavacode. Usually,youstarttheJRunserverbyopeningthecommandwindowandtypingthefollowing(it’s theWindowsversion): C:\fds2\jrun4\bin>jrun -start default
JRunusessettingsfromtheconfigurationfilejvm.configlocatedinthebindirectoryofJRun.Our goalistostartJRuninthedebugmode,andtodothiswe’llmodifythefilejvm.config. To load a reference implementation of JPDA, its manual (http://java.sun.com/j2se/1.5.0/docs/ guide/jpda/conninv.html)suggestsusingtheJVMoptionagentlib:jdwpforJava5.0,andwiththe pre-5.0JVMyoushoulduse–Xdebugtoenabledebuggingand–Xrunjdwp.Forexample,thejava. argslinemaylookasfollows: java.args=-Xms32m -Xmx384m -Dsun.io.useCanonCaches=false transport=dt_socket,address=8000,server=y,suspend=n
-Xdebug -Xrunjdwp:
Inthelineabove,-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=ninstructsthe JVMtouseasocketasatransportandlistenforaconnectiononport8000.Theserverdoesn’tneed tobesuspendedwhilewaitingforadebuggerapplicationtoconnect. AfteryourJRuninstanceisstartedinthedebugmode,youneedtocreatetheEclipsedebugcon-
534
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
figurationforthisremoteJavaapplication.SelectthemenusRun|Debugandright-clicktheoption RemoteJavaApplication(seeFigure12.4).
Figure12.4Creatingthenewdebugconfiguration
InthepopupwindowenterthenameoftheEclipseJavaprojectyou’dliketodebugthatcontainscode deployedunderJRun,asinFigure12.5.Pleasenotethattheportnumbershouldmatchtheonespecified inJRun’sfilejvm.config.Asanexample,weareusingtheJavaOrderEntryapplicationfromChapter7.
Figure12.5MappingtheEclipseJavaprojecttoJVM’sJPDAport
RIAWITHADOBEFLEXANDJAVA
535
CHAPTER12
Thisisprettymuchit.NowyoucansetyourbreakpointsintheJavasourcecode,whichhastocorrespondtothecodedeployedinJRun. Figures12.6and12.7showthesnapshotsoftheEclipsedebuggerperspective,illustratingbreakpointsinbothActionScriptandJavasourcecode.
536
Figure12.6AtthebreakpointintheActionScriptcode
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Figure12.7AtthebreakpointintheJavacode
UsingEclipseWTPforDebuggingJavaEEServers OurnextgoalistolearnhowtostartyourJavaEEapplicationserverinsideEclipse.Thistimewe’ll useApacheTomcat5.5,whichisavailablefordownloadathttp://tomcat.apache.org/download55.cgi. If we are able to start the server inside Eclipse, the rest is easy: just set your breakpoints in the server-sideJavacodeandoffyougo. Therecouldbedifferentwaysofdoingthis–insomecasesit’sjustamatterofconfiguringyour server’sstartupruntimeinEclipse.AnalternativewayistofindandinstalltheEclipsepluginfor yourJavaEEserver. AmoregenericwayistouseanopensourceEclipseprojectcalledtheWebToolsPlatform(WTP), availablefromtheEclipseFoundationatthefollowingWebsite:http://www.eclipse.org/webtools/. WhileEclipseWTPaddsvarioususefultoolsforthedevelopmentofJavaEEandWebapplications, we’lljustexplorehowitcanhelpusdebugJavacodedeployedinapplicationservers. TheeasiestwaytogetWTPistodownloaditsall-in-onezipfilefromhttp://download.eclipse.org/ webtools/downloads/.ItcontainstheEclipseIDEwithallrequiredplugins.Atthetimeofthiswriting,WTP1.5.xismeantforEclipse3.2andWTP1.0.xworkswithEclipse3.1. RIAWITHADOBEFLEXANDJAVA
537
CHAPTER12
ToinstallthisversionofEclipsewithpreconfiguredWTP,simplyunzipthecontentsofthisfileon yourdiskdrive.ReinstalltheFlexBuilderpluginandstartEclipse,pointingattheworkspacewith yourFlexprojects.
ConfiguringTomcat AfterinstallingWTP,you’llfindanewperspectivecalledJ2EE.Openit,gotothemenuWindows| Preferences,andyou’llfindsomenewitemsthere–oneofwhichiscalledServer.Let’saddTomcat asourdefaultserverbyselectingInstalledRuntimesundertheServeritem.AfterpressingthebuttonAddyou’llseeapopupwindowlistinganumberofJavaEEserverssuchasWebLogicorJBoss.If youdon’tseetheoneyou’relookingfor(i.e.,IBM’sWebSphere),clickonthe“Don’tseeyourserver listed?ClickHere”linkshowninFigure12.8.
Figure12.8AddinganewserverEclipse
SelectApacheTomcat5.5,andspecifyitsinstallationdirectoryasinFigure12.9.
538
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Figure12.9ConfiguringTomcatasadefaultserver
CreateanewDynamicWebprojectcalledPortfolio_Java,whereTomcatisatarget(menusFile| NewProject|Web|DynamicWebproject). We’ll askWTP to create a JavaServer Page file HelloWorld.jsp to pay respect to HelloWorld and ensurethatwecandebuganyJavaWebapplication.JustimaginethatwearenotdebuggingHelloWorld,buttheOrderEntryfromChapter7,orTickerFeed.javafromChapter5. OpenJ2EEperspectiveandright-clickontheWebContentunderPortfolio_Javaproject.Selectthe menusNew|JSP,enterthenameHelloWorld,andtheboilerplateJSPcodewillbecreatedinasecond. We’lljustaddonelinebetweentheHTMLand tagsandsetabreakpointonthisline(seeFigure12.10).
RIAWITHADOBEFLEXANDJAVA
539
CHAPTER12
Figure12.10HelloWorld.jsp
Finally,let’sstartHelloWorld.jspunderTomcatinthedebugmode:right-clickonHelloWorld.jsp andselecttheoptionsDebugAs|DebugonServer.TheseactionswillstartTomcatinsideEclipse, andit’llopenthedebuggerperspectiveassoonasitreachesourbreakpoint.Thescreenshotfrom Figure12.11looksfamiliar,doesn’tit?
Figure12.11DebuggingHelloWorld.jspundertheTomcatserver
IfyoujustwanttostarttheserverwithoutrunninganyWebapplication,opentheserver’sviewin theJ2EEperspective,right-clickontheserver,andpresstheStartbutton.
540
RIAWITHADOBEFLEXANDJAVA
LoggingandDebuggingFlexandJavaApplications
Figure12.12StartingtheJavaEEServerinEclipseWTP
Inthissectionwe’vebeenusingApacheTomcat,butthesamedebuggingtechniquesworkforother JavaEEserversaswell.
DeployingaWebApplicationLocally Yourchoiceofdebuggingtechniquesdependsonthedevelopmentenvironmentyourteamworks in.AtypicalprojectconsistsofFlexandJavaportions,andtheWebapplicationisbeingbuiltand deployedusingacommonbuildscript(i.e.,Antbuild.xml).Thisbuildmayproducejustone.war filethatisdeployedinaJavaEEapplicationserver.WhatifyouareexperiencingerrorsinyourFlex portionthatdidnotsurfacepriortothefinalFlex-JavaAntbuild? Inthiscase,installandruntheJavaapplicationserveronyourdevelopmentmachine,unzipthe .warfileintoaseparatedirectory,anddeployitintheexplodedformfromthisdirectory.Makesure thattheFlexBuildercompiler’soptionsofyourprojectincludetheoptionservicespointingatthe services-config.xml. Note:ThissolutionwillworkifyourFlexBuilderprojecttypeisFlexDataServices.
Summary It’s hard to overestimate the importance of debugging and logging in distributedWeb applications. Inthischapterwe’veshowndifferenttoolsandtechniquesthatcanhelpyoutopreventorlocalize andfixdevelopmentandproductionproblemsinFlex/Javaapplications.
RIAWITHADOBEFLEXANDJAVA
541
542
RIAWITHADOBEFLEXANDJAVA
CHAPTER
13
BuildingaSlideShowApplication ByBenStucki
RIAWITHADOBEFLEXANDJAVA
543
CHAPTER13
BuildingaSlideShowApplication
Inthischapterwe’llbebuildinganarratedslideshowapplicationinFlex2thatwillusethepopular Flickr(www.flickr.com)WebServicestoretrievethephotos.UserswillalsobeabletoinputacustomURLasthephotooraudiolocationandhaveafewextrasettingsandimageeffectsthatcan beappliedatruntime.Whenusersaredonecreatingslideshows,theapplicationwillprovidethem withaURLthatotherscanusetoviewtheresult. ThisapplicationmaybehandyforFlickruserswhowanttosharetheirpersonalphotoswithfriends andfamilyinaslideshowformat,butitcouldbeusedforanumberofbusinesspurposesaswell. Forexample,arealtycompanymaywanttodisplayphotosofapropertynarratedbythelocalrealtor,oracardealershipmaywanttodothesameforitscars.Somebusinessesmayevenusethe applicationtogivepresentationssimilartothoseMicrosoftPowerPointwouldprovide. Nomatterwhattheendresultis,wewantuserstoexperienceRIAsattheirbest.We’llmaketheapplicationsothatusersspendnomoretimecreatingaslideshowthantheywouldspendwatching it.Hopefully,withcorrectplanning,you’llspendnomoretimedevelopingitthanyouwouldspend readingthischapter.Fortunately,Flex2makesbothofthesegoalspossible.Let’stakealookatwhat we’llbebuilding.
ApplicationOverview Theapplicationwillhavetwoprimarystates:designandpreview.We’llstartbydevelopingafew componentsofthepreviewportionoftheapplication.Itwillcontainallthefunctionalityneededto viewandnavigateaslideshow,includingthetimingcontrol,audioplayback,imageeffectsprocessing,andathumbnailpreviewofeachimagefornavigation.Thedesignstatewillletusersexplore photosfromflickr.com,addandremovephotosfromtheslideshow,includeaudio,andsetthetimingandeffectsforeachphoto(seeFigure13.1).
544
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
Figure13.1Theapplication’sdesignstate
LikemostRIAs,thisapplicationisgoingtoincludeanumberofcustomcomponentsthatareused togethertocreateauniqueexperience.Thesecomponentsincludethethumbnailscrollershownin Figure13.1aswellastheSlideShowcomponentthatwe’llbedevelopinginamoment.We’realsogoingtocovertimerevents,workingwiththeHTTPServicecontrolandcreatingrichuserinterfaceswith ActionScript3.We’vegotalottogoover,solet’sgetstartedbydesigningtheSlideShowcomponent.
DevelopingtheSlideShowComponent The slideshow component encapsulates all the functionality needed to play and navigate a slideshow.StartbycreatinganewfilenamedSlideShow.mxmlinyourcomponentdirectoryofchoice.In thisbookwe’rekeepinguserinterfacecomponentsattherelativepathcom/theriabook/controls,so therelativepathfromtheapplicationtothisMXMLfilewouldbecom/theriabook/controls/SlideShow.mxmlforme.InitssimplestformtheSlideShowcomponentisjustanImageinsideaCanvas asshowninListing13.1.
Listing13.1ThefirstversionoftheSlideShowMXMLcomponent
RIAWITHADOBEFLEXANDJAVA
545
CHAPTER13
NoticethatI’vesetthehorizontalScrollPolicyandverticalScrollPolicyattributestooffontheCanvasandhavegiventheImageanIDofimage.We’regoingtobeaddingsomeActionScripttothis componentlater,soit’simportantthatwecanmodifytheimagethroughcodewithoutcreating unnecessaryscrollbars.I’vealsoincludedsomestylingattributeswiththiscomponenttomakeit lookalittlemoreSlideShow-like.TheseattributesareavailablebecausetheyarepartoftheCanvas component, which SlideShow is inheriting, but they can be overridden when the component is includedinanapplication. Totestourcomponentwe’llhavetocreateanapplication.Inyourapplicationdirectory,createa newfilenamedSlideShowPlayer1.mxml.Simplydeclareyourcomponent’snamespaceandusethe filenameastheMXMLelementasshowninListing13.2.
Listing13.2IncludinganMXMLcomponentinanapplication CompileandrunyourapplicationandyoushouldseealargeblankCanvas(seeFigure13.2)with thestylingattributesyou’vesetinthecomponent.
Figure13.2RunningtheSlideShow1.mxml
We’regoingtoaddsomeActionScripttotheapplicationandSlideShowcomponentinawhiletoget ourimagesworking,butfirstweneedtotakealookathowslideshowdataisrepresented.
546
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
LoadingSlideShowData ForthisapplicationaslideshowisrepresentedbyanyXMLfilewitharootnodeofslideshowin whichimagesarerepresentedbyaphotonodeandsoundfilesbyanaudionode.Anychildnodes oftherootshouldincludeasourceattribute(inwhichaURLwithimagesandaudiofilesisprovided),andphotonodesshouldincludeadurationattribute(inwhichatimedurationmeasuredin secondsisprovided).ImagesmustbeJPEG,PNG,GIF,orSWFfiles,andaudiomustbeanMP3file. CreateanewXMLfilenamedtest.xmlinyourapplicationrootandpopulateitwithslideshowdata suchastheXMLshowninListing13.3.
Listing13.3SlideShowXMLdata Inthischapterwe’reonlydisplayingasingleimageatatimeandusingasingleaudiotracktosynch withtheimages,sotheaboveXMLformatisallthedataweneed.However,thisformatcouldbe expandedtocreateamoreadvancedapplication. Now that we’ve got some slideshow data, let’s pull it into the application using an HTTPService objectinMXMLandfeedtheresultintoourSlideShowcomponentasshowninListing13.4.
Listing13.4SlideShowPlayerapplication
RIAWITHADOBEFLEXANDJAVA
547
CHAPTER13
Whenourapplicationstarts,itwillnowmakearequestfortest.xmlandpasstheresulttotheSlideShow’sloadShowmethod.However,theSlideshowcomponentfromListing13.1doesn’thavealoadShowmethodyet.We’llneedtoaddthismethodbycreatingthepublicfunctioninthecomponent file.There,wecanalsoaddthepreloadingfunctionalityrequired.We’llneedtoincludeaProgressBar MXMLcomponentandsomevariablestoholdtheloadedcontentasshowninListing13.5.
Listing13.5ThesecondversionoftheSlideShowcomponent:SlideShow2.mxml
Inthecodeabovewe’veusedActionScripttocreateaLoaderobjectforeachimageandstoreitinan ArrayCollection.TheloadShoweventkicksthingsoffbyloadingthevalueofthesourceattributefor RIAWITHADOBEFLEXANDJAVA
549
CHAPTER13
eachphotonode(representedbythevariablephotoNode)intoanewLoaderobjectandattachingevent listenersfortheProgressandCompleteevents.TheProgresseventwillbebroadcasteachtimeasubstantialchunkofdatahasbeendownloadedfromthefile.ThisletsusupdatetheProgressBarMXML componentprogrammaticallytodisplaythedownloadprogress(providedbytheLoaderobject)tothe user.Whentheimagesaredoneloading,theCompleteeventtriggerstheonCompletemethod,which loadstheaudiofileinthesamemanner.Preloadingmakesaudioandimagesyncingmuchsimpler. Sinceweknowtheaudioandimageswillbeginplayingatthesametimeandthatnewimageswill displayimmediatelywhenrequested,wecansimplyrepresentsyncingbyassigningatimedurationtoeachphoto.Whenalltheaudioandimagesareloaded,wedisplayaLinkButtonPlaytothe user.Theplayfunctionalityisn’timplementedyet,butyoucancompileandrunthisapplicationto testtheloadingandprogressbarfunctionality(seeFigure13.3).
Figure13.3RunningSlideShow2:loadingimagesandtheprogressbar
AnimatingtheSlideShow Togetourslideshowrunning,weneedtocreateaTimerobjectthatcoordinatestheimagesand haslogicinitsTimereventtochangetheimagesourceatthespecifiedtimeintervals.Let’screate amethodcalledplayShowtoinitializeandstartthetimerandsettheplaybutton’sclickeventto invokethismethod. private var timer:Timer = new Timer(1000,0); private var photoIndex:int = 0; private var nextPhotoTime:int = 1;
550
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication public function playShow():void { if(isLoaded) { play.visible = false; timer = new Timer(1000,0); timer.addEventListener( TimerEvent.TIMER, onTime ); timer.start(); if(sound!=null) {sound.play();} } else { loadShow( _xml ); } } private function onTime( event:TimerEvent ):void { if( event.currentTarget.currentCount == nextPhotoTime ) { if( photos.length > photoIndex ) { image.load(Loader(photos[photoIndex]).content); // using e4x to access the photo duration attribute nextPhotoTime += int(_xml.photo[photoIndex].@duration); photoIndex++; } else { stopShow(); } } } public function stopShow():void { timer.stop(); timer.reset(); image.source=””; SoundMixer.stopAll(); photoIndex = 0; nextPhotoTime = 1; play.visible = true; }
Listing13.6SlideShow3:thetimerandplayfunctionality We’vealsoincludedafunctiontostoptheslideshowanddisplaytheplaybuttonagainoncethe slideshowhasfinished.Thiswillletusersreplaytheslideshowaftertheirfirstviewing,butthere’s stillnowaytonavigatefromslidetoslidewhileyou’rewatching.
RIAWITHADOBEFLEXANDJAVA
551
CHAPTER13
Figure13.4RunningtheSlideShowPlayer3.mxml
AfteraddingthecodefromListing13.6totheSlideShowcomponent,weneedtoaddthecalltothe playShow()methodintheSlideShowPlayer.mxml.Forexample:
AddingInteractiveThumbnailNavigation Sofarwe’veencapsulatedallthelogicneededtopreloadandplayaslideshow.Ourcomponenteven displaysgraphicalprogressindicatorsandsyncsimageswithaudio,butusersstillcan’tviewimage thumbnailsorskipaheadtotheslidethatintereststhemmost.Thisisanespeciallyimportantfeature ifthiscomponentistobeusedforbusiness.Let’screateanotherMXMLcomponentinthecom/theriabook/controlsdirectoryandputthecodefromFigure13.7inside.Alternately,youcoulddownload theScrollercomponentfromtheFlex2Exchangeathttp://www.adobe.com/exchange/.
[Event(“change”)]
0) { this.selectedItem = this.contents.getChildAt(0); this.selectedIndex = 0; } } public var itemRenderer:IFactory = null; public var selectedItem:Object = null; public var selectedIndex:int = -1; private function init():void { this.addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame( e:Event ):void { if (this.mouseX >= 0 && this.mouseX = 0 && this.mouseY = 0 && momentum > 0) || this.contents.width < this.width ) { this.contents.x = 0; this.momentum = 0; } else if(this.contents.x + momentum
Listing13.7Scrollercomponentcode In this code the display objects are kept in a single container named contents so that they can be moved in unison. The set method for the dataProvider is used to clear any existing childreninthecontentsobjectandcreateanewinstanceoftheitemRendererforeachitem inthedataProvider.Thisway,thecomponentprovidessimilarfunctionalitytoFlex’sTileList component, but it has a significantly different user interface.The Scroller tiles each generatesaniteminahorizontalrowandprovidesautomaticscrollingbasedonmouseposition. ThisrichinteractivebehaviorisdefinedinsidetheonEnterFramemethod(whichisalistener for the ENTER_FRAME event). It moves the contents’ position based on the position of the mouse relative to the center (width/2) of the component. The constants SENSITIVITY and FRICTIONcanbeusedtoadjustthecomponent’smousesensitivityandspeeddegradation, respectively.It’sidealforourthumbnaildisplaysincespaceislimited.We’regoingtoadditto theSlideShowcomponentandbindittothedatawealreadyhavebyaddingsomeMXMLto ourSlideShowcomponentasshowninthecodesnippetinListing13.8(seethecompletecode oftheSlideShowinListing13.11).
Listing13.8AddingtheScrollercomponentinMXML NoticethatinListing13.8we’vereferencedcom.theriabook.controls.SlideThumbastheitemren-
554
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
derer.TheSlideThumbcomponentsimplydisplaysanImageinsideaCanvascontrol.Whenthe Scrollergeneratesthiscomponentitwillassigneachinstancethecorrectxmlnodeusingthedata property.WejustneedtobindtheImage’ssourceattribute(seeListing13.3)todata.@sourceasin Listing13.9andourcustomitemRendererwillloadtheimageasexpected.
Listing13.9SlideThumbcomponent We can also include rich interactive behavior by adding code to the SlideThumb’s enterFrame event, whichmeasuresthedistancefromthisimagetothemousepositionandusesthevaluetosettheimage’s width,height,andalphatransparencybetweenahard-codedmaximumandminimumvalue.Insertthe codefromListing13.10intheSlideThumbcomponentandusetheinitializedeventtoinvoketheinit function.Thiswillgiveeachthumbnailfluidresizingbehaviorbasedonmouseposition.
= 0 && this.mouseY 51 || this.image.width < 49) { this.image.width += (50 - this.image.width)/3; this.image.height += (50 - this.image.height)/3; } } ]]>
Listing13.10Thumbcomponentbehaviorcode
RIAWITHADOBEFLEXANDJAVA
555
CHAPTER13
All of the components for the SlideShowPlayer have been created, but we still need toaddcodethatwillnavigatetoanygivenslidebasedontheuser’sthumbnailselection. Let’screateagotoSlidemethodinSlideShow.xmlandusetheScroller’schangeeventtopass itthecorrectslideindex.ThemethodwillneedtoloopthrougheachnodeintheXMLand sum the duration where the slide at the given index begins. It will then reset the sound to play,startingatthecorrecttime,andsetthecurrentphotoIndexandnextPhotoTimetodisplaytheselectedphotoimmediately.ThefullcodefortheSlideShowcomponentisgivenin Listing13.11.
photoIndex ) { image.load(Loader(photos[photoIndex]).content); nextPhotoTime += int(_xml.photo[photoIndex].@duration); photoIndex++; } else { stopShow(); } } } public function gotoSlide( index:int ):void { var gotoTime:uint = 0; for(var i:uint=0;i
558
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
Listing13.11Slideshowcomponentsourcecode
DevelopingtheSlideShowPlayerApplication TofinishtheSlideShowPlayerwestartedearlier,let’smodifythecodetoacceptaQueryStringparameter named slideshow and pass its value to the HTTPService.To do this we’ll reference the Application.application.parameters object, which contains any query string name-value pairs fromtheURLusedtoreferencetheSWFfile(notethatthismaynotbetheURLusedtoreference the currentWeb page). Parameters can be specified directly in the URL as key-value pairs right afterthequestionmark(http://myserver.com/myApp.swf?slideshow=”someURLofYourShow”)or embeddedintheHTMLwrapperusingflashvarsparameters. Inthisexamplethequerystringparameterwe’relookingforisnamedslideshow.Tospecifyyourown slideshow,simplysetslideshowequaltoaURLthatpointstoavalidslideshowXMLfile.Thefullcode fortheSlideshowPlayerisgiveninListing13.12,andtheresultingSWFisshowninFigure13.5.
RIAWITHADOBEFLEXANDJAVA
559
CHAPTER13
Listing13.12SlideshowPlayerapplicationsource
Figure13.5SlideshowPlayerapplication
DevelopingtheSlideShowCreator We’vealreadygotagreatslideshowplayer,butwestillhavetocreateanapplicationthatletsusers maketheirownslideshow.Togetstarted,let’screateanewMXMLfilenamedSlideShowCreator. mxml:
560
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
Listing13.13AfragmentoftheSlideShowCreator.mxml In Listing 13.13, we’ve created a Panel area to search and select images based on a given tag. It includesaTileListtoholdtheimagesandaControlBarwithtextinputforsearchterms.Nowwe needamethodforretrievingimagesbasedontheuserinputgiven.Thevariableservice(seeListing13.14)representsaWebServiceofthepopularWebportalflickr.comthatletspeoplestoreand sharetheirphotos.
IntegratingwithFlickrWebServices Flickrprovidesdocumentationonhowtoconnecttoitsservices,andmanythird-partyproducts usethem(seehttp://www.flickr.com/services/).There’sanAPIathttp://www.flickr.com/services/ apianditsmanualsaysthatwecanusequerystringstopassoursearchtexttoaspecificURL.It willthenreturnanXMLresponsecontainingthedataweneedtoreferenceagivenimage.Itwill alsoletusknowthatwecanusetheimagesinFlexbecauseflickr.comhasputacross-domainXML fileintheappropriatedomain.ThisfileisrequiredtoallowanyFlash-basedapplicationtoaccess imagesoutsidetheWebdomaininwhichitruns.Basedonthatdocumentation,wecandefineand useanHTTPServicecontrolthatincludesthedynamicuserinputfromtheTextInputcontrolinits URLasshowninListing13.14
Listing13.14DefiningflickrHTTPServicewithatextparameter Notethatyou’llneedtoreplacethetermyour_api_keywithyourownAPIkeyobtainedfromFlickr. YoucanrequestonefromtheFlickrAPIsiteImentionedearlier. WhenarequestwiththisURLissenttoFlickr,wecanexpectanXMLfileinreturn.AsampleresponseisshowninListing13.15.
Listing13.15FlickrWebServiceresponse We’llneedtocreateanitemRendererfortheTileListthatusestheXMLdatareceivedfromFlickrto retrieveanddisplayimages.Let’screateanewMXMLcomponentnamedFlickrThumb:
Listing13.16FlickrThumb.mxml ThestringconcatenationusedisbasedonFlickr’sdocumentationathttp://www.flickr.com/services/api/misc.urls.html.Itshowsthatwecanexpecttoloadanimagethatis75pixelsinwidth and75pixelsinheightusingtheconstructedURL.Youcanfindtheattributesserver,ID,andsecret inListing13.15. Now that all the necessary components are in place we can bind the appropriate HTTPService result to theTileList in our SlideShowCreatorapplicationbysettingitsdataProviderpropertyto “{service.lastResult.rsp.photos.photo}”. The“rsp.photos.photo” portion of this code is referencing the actual XML response from Flickr. Let’salsosettheitemRendererasFlickrThumbandthesearchbutton’sclickeventtoinvokethe HTTPServiceobject’ssendmethod.RuntheSlideSlowCreatorapplicationtotestourFlickrintegrationandsearchthroughimagestoyourheart’scontent.Theprogamwilloutputthescreen(withoutphotos)asinFigure13.6.
562
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
EditingSlideShowData We’vefinallygotlotsofFlickrimagestolookat,butusersstillcan’tdoanythingwiththem.Weneed tocreateanXMLobjecttoholdourslideshowdataaswemanipulateitandasecondonetohold thecurrentlyselectedphotodata.Thenwecancreatemethodstoselect,add,andremoveslides basedontheirURLasshowninListing13.17:
; [Bindable] public var selected:XML = ; private function selectSlide( source:String, duration:uint ):void { var slide:XML = ; slide.photo.@source = source; slide.photo.@duration = duration; selected = slide; imgDuration.value = duration; } private function addSelectedSlide():void { selected.photo.@source = image.source; selected.photo.@duration = imgDuration.value xml.appendChild( selected.photo ); } private function removeSelectedSlide():void { xml.photo = xml.photo.(@source!=selected.photo.@source); } ]]>
Listing13.17ManipulatingXMLdatainActionScript Onceagain,we’veusedE4XinthiscodetostoreandmanipulateXMLdata.Ifthiswasalargeror more complex application it might have been necessary to create business objects instead, but sincetheslideshowdataisultimatelyrepresentedasXML,thismaybeabetteroption. Now we’ve got all the methods needed to create a slideshow.We just need to fill in some of the RIAWITHADOBEFLEXANDJAVA
563
CHAPTER13
user interface required. Inside the Panel we’ll create aVBox containing an Image control and a Formwithinputforthephotosourceandduration.ThisshouldincludeaTextInputcontrolcalled imgSourceandaNumericSteppercontrolnamedimgDuration.WecanbindtheImagecontrolto “selected.photo.@source”tobesureitalwaysshowstheselectedimage.We’vealreadyreferenced theNumericStepperimgDuration(justatextfieldwithupanddownarrows)inthecodeaboveso that it updates correctly.We’ll also need to invoke the selectSlide method inside of theTileList’s changeeventandtheaddSelectedSlideandremoveSelectedSlidemethodsinsideofbuttonclick eventsasshowninListing13.18.
Listing13.18PanelwithImageselectionandcontrol
564
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
Nowthatwe’vegotsomeoftheinterfaceinplace,userscanaddandremovephotostoandfrom theslideshowXMLobject,butthere’sstillnofeedbacktoshowusersthecurrentlistofslides.Fortunately,we’vealreadycreatedaScrollercomponentandThumbcomponentthatcanbereusedand boundtotheslideshowXML.SimplyaddtheScrollercomponentwecreatedearlierunderneath thePanelandbindittotheXMLobjectasshowninListing13.19.
Listing13.19AddingtheScrollercomponent Noticethatwe’vealsosettheScroller’schangeeventtoinvoketheselectSlidemethod.Thisisthe onlyinteractivityweneedtodefine.Flex’srichdatabindingwilltakecareofupdatingthelistwhen itemsareaddedorremoved.Runyourapplicationandbesurethateverythingisinworkingorder. YoushouldbeabletobrowsephotosfromFlickr,selectphotostopreview,adjustsettings,andadd photostotheslideshowlist.Youshouldalsobeabletoselectphotosfromtheslideshowlistand removethem.ThelastthingweneedtoincludeisthecapabilitytoaddaudiofromaURL.Insertthe codefromListing13.20intotheControlBarcomponentalreadyintheapplication.
Listing13.20HTTPService InListing13.20we’veprovidedaTextInputcontrolfortheusertoenteraURLandabuttonwhose clickeventsetstheappropriatevalueinourslideshowXML.Alsonotethatwehavesurrounded theseinputfieldswithaVBoxcontainerthathasahorizontalAlignvalueofright.Thisisprovided sothatthecontrolsalignthemselvesontheoppositesideoftheFlickrsearchcontrols. Forexample,enterNewYorkintheTagstextfieldandpressthebuttonSearchFlickr.TheSlideShowCreatorwilldisplaythethumbswiththeimagesofthephotosofNewYork.Selecttheimage thatyou’dliketoincludeinyourslideshow,andyou’llseetheURLofthesourcefile.Enterrequired duration.PressthebuttonAddPhotoandrepeatthisprocedureforotherphotos. Nowthatwe’vedevelopedawayforuserstocreateaslideshow,let’sincludeawayforthemtopreviewitintheSlideShowCreatorapplication.
RIAWITHADOBEFLEXANDJAVA
565
CHAPTER13
Figure13.6TheSlideShowCreatorwindow:Designmode
DevelopingtheSlideShowPreview Topreviewslideshows,wecansimplyreuseourSlideShowcomponent.We’llneedtocreateanew viewstateandincludeawayforuserstonavigatebetweenbothviewstatesintheapplication.To createanewviewstatewecanusetheMXMLStatecontrolasshowninListing13.21.
Listing13.21Statecontrol In this section of the code we’ve defined a new state named preview. In the preview state we’re settingthePanelandScrollercontrolstobehiddenandcreatinganewSlideShowcomponenton stage.We’vealsosettheenterStateandexitStateeventstoplayandstoptheslideshow,respectively. Nowallweneedtodoisprovidetheuserwithawaytonavigatetotheappropriateapplication state.We’veleftsomeroomatthetopofourapplicationforanApplicationControlBarandwe’re
566
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
goingtouseittoprovidelinkstoourtwoapplicationstatesasshowninListing13.22.
Listing13.22Statecontrol IntheclickeventoftheLinkButtonforourDesignviewwe’vesettheapplicationviewstateback tothedefaultviewbyassigningcurrentStateequaltoablankstring.Runtheapplicationtocreate anewslideshowandpreviewitusingourSlideShowcomponent.Ifyouhaveanytroublerunning yourapplication,referencethefullSlideShowCreator.mxmlsourceshowninListing13.23.
; [Bindable] public var selected:XML = ; private function selectSlide( source:String, duration:uint ):void { var slide:XML = ; slide.photo.@source = source; slide.photo.@duration = duration; selected = slide; imgDuration.value = duration; }
RIAWITHADOBEFLEXANDJAVA
567
CHAPTER13 private function addSelectedSlide():void { selected.photo.@source = image.source; selected.photo.@duration = imgDuration.value xml.appendChild( selected.photo ); } private function removeSelectedSlide():void { xml.photo = xml.photo.(@source!=selected.photo.@source); } ]]>
568
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
Listing13.23SlideShowCreator.mxml Whenourapplicationisrunninginpreviewmode,theSlideShowcomponentshouldautomaticallyload andletusersinteractwithitjustastheywouldintheSlideShowPlayerapplication.SeeFigure13.7. WhenallslidesareselectedandanMP3audiofileisready,clickonpreviewandenjoytheshow!
Figure13.7SlideShowCreatorPreviewstate
RIAWITHADOBEFLEXANDJAVA
569
CHAPTER13
Summary In this chapter we’ve developed an application that can be used to create and preview custom slideshowsusingtheFlickrAPI.Hopefullywe’vegottenyouwellonyourwaytocreatingaveryrich andusefulapplicationinFlexwithrelativelylittleeffort.We’vealsohadanotherchancetoreview severalimportantFlex2topicsincluding: • • • • • •
Developingcomponentsforreuse AnimatingwithActionScript3 UsingTimerevents IntegratingwithWebServices UsingE4Xwithdatabinding Managingviewstates
Flex2makesdesktop-likeapplicationsarealityfortheWeb.Wehopeyoucanexpandontheideas presentedinthischapterandcreateyourownmediaapplicationstargetedfortheWeb.
570
RIAWITHADOBEFLEXANDJAVA
BuildingaSlideShowApplication
RIAWITHADOBEFLEXANDJAVA
571
572
RIAWITHADOBEFLEXANDJAVA
CHAPTER
14
DevelopingCustomCharts
RIAWITHADOBEFLEXANDJAVA
573
CHAPTER14
DevelopingCustomCharts
InChapter5weusedacomponentinasampleportfolioapplication.FlexCharting letsyounotonlyofferbettervisualizationofyourdata,butalsocreateinteractivedashboard-type applicationsinwhichthechartscanrespondtoauser’sactions.Forexample,theuser’sclickona piesliceintheportfolioapplicationwouldrepopulatethedatagridwiththenewsaboutselected stock. CheckouttheGoogleFinanceapplicationathttp://finance.google.com/finance.Justenterastock symbol(e.g.,ADBE)andenjoyworkingwithahighlyinteractivelinechartrepresentingthestock performanceoveraselectedperiodoftime.
574
Figure14.1GoogleFinancewithinteractivecharting
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts
ThislinechartisrenderedbytheFlashPlayeranditreactstoyourmousemovements,displaying thedateandtimeunderthemousecursor;ithasazoomfeature;andyoucanchangethetimeperiodbydraggingthetimelineabovethechart.ThisFlashchartissmartenoughtointeractwiththe restofthenon-FlashWebpagecomponents.Clickononeoftheselittleflagsandthecorresponding news item is highlighted on the right. Interactive charts will give your end users a lot more controlthanstaticones,andwillmakethemhappy. In this chapter, we’ll show you how you can program similarly interactive charts by creating yourownandextendingstandardFlexChartingcomponents.Weassumethereaderisfamiliar withthebasicsofFlexCharting.TogetafeelingforwhatFlexChartingisabout,checkoutthe followingURLthatdemosvarioussamplesofchartsinactionandshowstheMXMLsourcecode for each: http://flexapps.macromedia.com/flex15/chartexplorer/explorer.mxml?versionCheck ed=true. Tosimplifydigestingthismaterial,we’llbuildthefollowingfiveapplicationsadding,complexity aswego: • • • • •
Aprogramthatdrawsarectangulararearightwithinthelinechartcomponent Alinechartwithahundreddatapoints Alinechartwithtwodataseriesandamovableverticalline Thelinechartdescribedabovewithatooltipdisplayingdatapointvaluesfromtwoseries Alloftheaboveplusachartzoomingfeature
Whiletheseapplicationswon’thaveallthefunctionalityGoogleFinanceoffers,it’lldefinitelygive youalittlepushintherightdirection.
HowtoDevelopaChartfromScratch Flexprovidesalargenumberofpre-builtchartsandflexiblewaystocustomizethem,insomecases youmightfindthatdevelopingyourownchartshasitsbenefits.Itgivesyoutheflexibilitytoimplementthefunctionality,rangingfromsimplyhighlightingareasofthecharttoprovidingsuperior interactivityandpresentation. Inourfirstexample,we’lltrytooverridethestandardpaintingofachartcomponent.Let’sstart with a simple example that will only use ActionScript. Suppose you want to implement limits –eachlimitrepresentingsomerectangularregiononthechartfilledwithsomecolor.Thiscanbe usefulinhighlightingsomebusiness-specificrangeofvalues.Forexample,ifastockpricedrops belowsomeresistancevalue,thelinechartshouldbedisplayedinaredarea.We’llcreatetwoAS3 classes: • •
ACartesianLimitthat’sthedatamodelofthelimit AnExtendedCartesianChartthat’sresponsibleforthelinechartandthelimit-areapainting
Pleasenotethatinthisexample,we’repaintingthisarearightonthesamegraphicobject.Laterin RIAWITHADOBEFLEXANDJAVA
575
CHAPTER14
thischapterwe’lluseadifferenttechnique(programmaticskins)andcreateadditionalobjectsto beputonthechart. Tobeginwith,let’screateaCartesianLimitclassthatwillholdallthevaluesrequiredtopaintthe limitsarea: Package com.theriabooks.limits { import mx.graphics.IFill; import mx.graphics.SolidColor; import mx.graphics.IStroke; import mx.charts.chartClasses.CartesianChart; import flash.geom.Point; import flash.geom.Rectangle; public class CartesianLimit { public var minX: Object; public var maxX: Object; public var minY: Object; public var maxY: Object; public var fillStyle:IFill = new SolidColor(0xFF0000); public var lineStyle:IStroke; public function paint(chart: CartesianChart): void { var pt0 : Point = chart.dataToLocal(minX,minY); var pt1 : Point = chart.dataToLocal(maxX,maxY); var x: Number = Math.min(pt0.x,pt1.x); var y: Number = Math.min(pt0.y,pt1.y); var width: Number = Math.abs(pt0.x-pt1.x); var height: Number = Math.abs(pt0.y-pt1.y); if(lineStyle != null) lineStyle.apply(chart.graphics); else chart.graphics.lineStyle(); if(fillStyle != null) fillStyle.begin(chart.graphics, new Rectangle(x,y,width,height)); chart.graphics.drawRect(x,y,width,height); if(fillStyle != null) fillStyle.end(chart.graphics);
576
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts } } }
Listing14.1TheActionScriptclassCartesianLimit.as Thisclassletsyoustoretheinformationabouttheregioncoordinates,fill,andthelinestyleused torenderthisregion.Themethodpaint()herecomputesthecoordinatesoftherectanglebased ontheprovidedminimumandmaximumvaluesandpaintstheareaaccordingtothefillandline style.ThisisnotacallbackasJavaprogrammersmightassume;we’llcallitfromtheclassExtendedCartesianChart.ThismethodhasoneargumentoftheCartesianCharttype,whichisabaseFlex classofallstandardtwo-dimensionalrectangularcharts. Theuser-definedActionScriptclassescanbeusedasMXMLtagsasinListing14.4. Object-orientedpuristsmaynotlikethefactthatwedidn’tdefinetheproperties(minX,maxX,et al)oftheprivateCartesianLimitclasswithpublicgettersandsetters.Ifwewentthatroute,we’d havetoclutterourcodewithabunchofthefollowingstatements(onepereachprivateproperty): [Inspectable] private var _minX: Object; public function get minX() : Object { return _minX; } public function set minX(value:Object) : void { _minX=value; }
Listing14.2Privatepropertieswithgettersandsetters Butifthesegettersandsettersdon’tincludeanyadditionalprocessing,butarejustpassingthedata fromtheoutsideworldtoprivatevariablesandback,wecangetawaywithoutgettersandsetters. Youdon’thavetoabidebytheJavaBeanspecificationtomakeyourcodevisibletotoolslikeFlex Builder. TohelpFlexBuilderprovidecodehints,youcanmarktheprivatepropertiesoftheActionScript classwithametatag[Inspectable].Forpublicpropertiesthiswouldn’tberequired.FlexBuilder’s MXMLeditorcanofferyoucodehintsasshownbelow:
RIAWITHADOBEFLEXANDJAVA
577
CHAPTER14
Figure14.2FlexBuildercodehintingfortheuser-definedproperties
Tocreateourcustomchart,weneedtoextendtheCartesianChartclass(abaseclassforallrectangular2Dcharts),andoverrideitscallbackmethodupdateDisplayList.FlexcallstheupdateDisplayList()methodwhenthecomponentisaddedtoacontainerusingtheaddChild()method,and whenthecomponent’sinvalidateDisplayList()methodiscalled.Don’tmissthefactthatthemethodupdateList()firstcallsitspeerfromthesuperclasstoprovidethedefaultdrawingofthechart, andthenpaintsthelimitsareaontopofit. We’vealsoaddedanewpublicpropertylimitstothisclass: package com.theriabook.limits { import mx.charts.chartClasses.CartesianChart; public class ExtendedCartesianChart extends CartesianChart { [Inspectable(category=”Data”,arrayType=”com.theriabook.limits.CartesianLimit”)] public var limits: Array;
} }
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if(limits != null) for(var i:int =0;i
Listing14.5Thelinechartwith100datapoints:LineChart100.mxml
RIAWITHADOBEFLEXANDJAVA
581
CHAPTER14
Thiscodewillproducealinechartthatmaylooklikethis:
Figure14.4Thelinechartwith100datapoints
Unfortunately,aswestartmovingthecursoroverthechart,werecognizethatthere’sanissue–the tooltipisshownonlywhenweputourcursorovertheactualdatapoint(shownasalittlecircle). Evenwith100pointsitmakesextractingthedatafromthechartdifficult.Andwhatifwewantto plotmultipleseriesandcomparevaluesatthesamedate?
AddingaVerticalLine Ideallywe’dliketoshowtheverticallinemovinghorizontallyoverthechart,followingyourcurrent mouseposition.We’dalsoliketoadddatatipsshowingthedataformultiplestocks(assumingthat eachlinerepresentssomestockprices)attheselectedpointintime.
582
Figure14.5Atwo-stockchartwithaverticalline
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts
Let’ssubclassourchartandimplementthisfunctionality. We’llsplittheimplementationintomultipleclassesfordemonstrationpurposes.Thefirstversion ofourprogramwilldisplayalinechartwithtwodataseriesemulatingthedataontwostocks.We’ll alsodisplaytheverticallineinthechartasshowninFigure14.5. Atthispoint,wearegoingtocreatetwoActionScriptclasses: •
ExtendedLineChart:ThisclassextendstheLineChartandisresponsiblefordrawingtheline chart
•
VLineLayer:ThisclassextendstheProgrammaticSkinclassanditspurposeistodrawthe verticallineontopoftheExtendableLineChart.
Non-TypicalUseofSkinning SkinninginFlexcanbeimplementedbyrenderinggraphics(ajpgfile).ButskinscanalsoberenderedprogrammaticallyasanActionScriptclass. Originally,Flexskinningwascreatedtochangethelook-and-feelofthevisualpartsofthesame componenteasily(forexample,apopularmediaplayerWinAmpoffershundredsofskins).Butthis timewe’llusetheskinningcapabilitiesofFlexjusttodrawthelineontopofthechart.Wearen’t planningtochangetheskinofthethinverticalline,butratherusethistechniquetoputanobject ontopofthechartandcontrolit(we’regoingtomovethislinealongthehorizontalaxis). Drawingalineisafairlysimpletask–we’lluseanewVlineLayerclassinheritedfromtheProgrammaticSkinclass.Thisclasswilldrawtheline(it’llplaytheroleofaskin)andadditasachildtoour chart. package com.theriabook.charts.line1{ import mx.skins.ProgrammaticSkin; import flash.geom.Rectangle; public class VLineLayer extends
ProgrammaticSkin {
protected override function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); var rect: Rectangle = (parent as ExtendedLineChart).viewBounds; graphics.clear();
RIAWITHADOBEFLEXANDJAVA
583
CHAPTER14
} }
} }
if(rect.x=parent.mouseX && rect.y=parent.mouseY) { graphics.lineStyle(1,0x808080); graphics.moveTo(parent.mouseX, rect.y); graphics.lineTo(parent.mouseX, rect.y+rect.height);
Listing14.6TheVlineLayerclassthatdrawsaline FlexdesignersdeclaredtheCartesianChart’spropertydataRegionthatdefinesthecoordinatesof theactualdatarectangleasprotected.Sinceweneedthisinformationtobeaccessiblefromthe outsideofthisinheritancehierarchy,we’vedefinedinoursubclassaviewBoundspublicproperty thatwillreturnthevalueofthedataRegiontoanyclassthatmayneedit: public function get viewBounds() : Rectangle { return super.dataRegion; } And the complete code for the ExtendedLineChart will look like: package com.theriabook.charts.line1{ import mx.charts.LineChart; import flash.events.MouseEvent; import flash.geom.Rectangle; public class ExtendedLineChart extends LineChart { private var skin: VLineLayer; public function ExtendedLineChart() { addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); } protected override function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); skin.width = width; skin.height = height; } protected override function createChildren() super.createChildren();
584
RIAWITHADOBEFLEXANDJAVA
: void {
DevelopingCustomCharts
}
} }
if(skin == null) addChild(skin = new VLineLayer());
public function get viewBounds() : Rectangle { return super.dataRegion; } private function onMouseMove(event: MouseEvent) : void { skin.invalidateDisplayList(); }
Listing14.7TheActionScriptclassExtendedLineChart Let’smodifyourMXMLapplicationfromListing14.5tosupportatwo-lineseries:
RIAWITHADOBEFLEXANDJAVA
585
CHAPTER14
Listing14.8LineChartVLine1.mxml Runthisprogramandyou’llseeachartthatlookslikeFigure14.5. Thisclassdrawsamovableverticalline,butwealsoneedatooltipthatcanshowthedataofmultipleseriesbasedonthepositionoftheverticalline.Thestandardtooltipwindowcanonlyshow theinformationaboutoneseries,sowe’llcreateanewclasstoimplementit(we’llomitsomepropertiesdeclarationstomakethecodemorereadable): public class ToolTip extends
UIComponent {
private var _borderWidth : uint = 1; private var _borderColor : uint = 0xff; private var _backColor : uint = 0xffff00; private var _backAlpha: Number = 0.5; private var textField : TextField = new TextField(); protected override function createChildren():void {
586
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts
}
textField.background = false; textField.x=2; textField.y=2; textField.selectable = false; addChild(textField);
protected override function measure():void { super.measure(); // add eight pixels around the text field in the tool tip to // just to look prettier measuredWidth = measuredMinWidth = textField.textWidth +2; measuredHeight= measuredMinHeight= textField.textHeight+2; } protected override function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number):void { textField.width=unscaledWidth-4; textField.height=unscaledHeight-4; graphics.clear(); graphics.lineStyle(_borderWidth, _borderColor); graphics.beginFill(_backColor, _backAlpha); graphics.drawRect(0,0,unscaledWidth, unscaledHeight); } public function get text() :String { return textField.text; }
}
public function set text(text:String) : void { textField.text = text; invalidateSize(); validateNow(); }
Listing14.9TheActionScriptclassToolTip We’lldisplaythetextinthetooltipusingatextcontol.Todothis,weoverridethecreateChildren method of the UIComponent, which sets no background, margins, makes it non-editable, and addsthetextasachildcomponent. Inthemeasurecallbackwe’lladdacoupleofpixelsmarginaroundthetext.
RIAWITHADOBEFLEXANDJAVA
587
CHAPTER14
Thenextstepistoimplementafunctionthatcantakeanx-coordinateandreturntheinformation aboutthenearestdatapointforeachseries.FlexChartssupporttwofunctionsforconvertingdata pointsintotheactualscreencoordinatesandviceversa–localToDataanddataToLocal.ButlocalToDatarequiresbothxandycoordinates,andwedon’tknowtheycoordinateofthepointatthe currentxsinceallweknowisthexcoordinateoftheverticalline.Thesolutionistocreatealistof allactualcoordinatesforallthedatapointsandthenuseittodeterminetheclosestdatapointsat agivenxcoordinate.Ourassumptionsare: • •
Allvaluesareprovidedinascendingorder(sortedbydate). Eachdataserieshasthesamenumberofdatapointsandthexcoordinateofeachdataseries isthesameforagivendate.
NowwecanimplementasimplePointCacheclassto“remember”theclosestdatapointsateach xcoordinate.ThemethodgetPointIndexByXwillreturntheindexofthedatapointinsidethedata series. public class PointCache { private var points : Array ; private var chart : ExtendedLineChart; public function PointCache(chart: ExtendedLineChart) { this.chart = chart; } public function reset() : void { points = null; } public function getPointIndexByX(x: int, index:int=0) : int { if(points == null) init(); if(!chart.viewBounds.contains(chart.mouseX,chart.mouseY)) return -1; var row: Array = points[index] as Array; for(var i:int = 0;i< row.length;i++) { if(row[i].x > x) { if(i == 0) return 0; // deltas are distances btwn the line and the closest ponts // on the left and on the right var delta0 : Number = x-row[i-1]; var delta1: Number = row[i]-x; return delta0
594
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts
Listing14.13ThefinalapplicationLineChartVLineZoom.mxml
Figure14.7RunningLineChartVLineZoom.mxmlbeforezoomingin
PresstheZoomInbuttonseveraltimes,andtheLinechartwillchangeasshownbelow.Thezoomingrationiscontrolledbythevalueyou’repassingtothezoom()functionunderthezoombuttons.
RIAWITHADOBEFLEXANDJAVA
595
CHAPTER14
Figure14.8RunningLineChartVLineZoom.mxmlafterzoomingin
Afteryouzoomintothechart,youcan’tseetheentiretimeinterval.Forexample,youdon’tseethe JanuarydataonFigure14.8.Hence,weneedtoimplementhorizontalscrollingsotheusercandrag themousetotheleftorright. For this scrolling we’ll introduce the member variables _ancorX and _ancorY in the ExtendedLineChartclassthatwillstorethelastmousepositionanddefinethemousedownandmouseup eventhandlersthatwilltoggletheisDraggingflag. OurnewonMouseMoveeventhandlerwillcomputetherequiredchangebasedonthenewandold mouselocationandadjusttheaxis’sminimumandmaximumaccordingly.Thefinalversionofthe ExtendedLineChartclassisshowninListing14.14. public class ExtendedLineChart extends LineChart { private static const MIN_RANGE: int= 7; private static const MSEC_IN_DAY:int = 24*60*60*1000; private var isDragging : Boolean = false; private var _ancorX: int; private var _ancorY: int; private var _cache: PointCache ; private var _layer: VLineLayer ;
596
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts private var _dataTip: ToolTip = new ToolTip(); private var tipDateFormatter: DateFormatter; private var tipNumberFormatter: NumberFormatter; public function ExtendedLineChart() { _cache = new PointCache(this); _layer = new VLineLayer(); addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); tipDateFormatter = new DateFormatter(); tipDateFormatter.formatString = “MMM DD, YYYY”; tipNumberFormatter = new NumberFormatter(); tipNumberFormatter.precision = 2;
}
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener(MouseEvent.MOUSE_UP, onMouseUp); addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); private function onMouseDown(evt: MouseEvent) :void { isDragging = true; _ancorX = mouseX; _ancorY = mouseY; invalidateDisplayList() ; } private function onMouseUp(evt:MouseEvent) :void { isDragging = false; invalidateDisplayList() ; } private function onMouseMove(event: MouseEvent) : void { if(!isDragging) { invalidateDisplayList(); return; } var axis: DateTimeAxis = DateTimeAxis(horizontalAxis); var min : Date = axisMin; var max : Date = axisMax; var vWidth : int = dataToLocal(axis.maximum).x -dataToLocal(axis.minimum).x; var change: int = (axis.maximum.getTime()-axis.minimum.getTime())*-(mouseX_ancorX)/vWidth; RIAWITHADOBEFLEXANDJAVA
597
CHAPTER14 var newmin: Date = new Date(axis.minimum.getTime()+change); var newmax: Date = new Date(axis.maximum.getTime()+change); if(newmin.getTime()max.getTime()) { newmin.setTime(newmin.getTime()+max.getTime()-newmax.getTime()); newmax = max; }
}
_ancorX = mouseX; _ancorY = mouseY; axis.minimum = newmin; axis.maximum = newmax; _cache.reset(); this.invalidateDisplayList();
public function zoom(delta: int ) : void { var axis: DateTimeAxis = DateTimeAxis(horizontalAxis); var newmin : Date= maxDate(axisMin,addDays(axis.minimum, var newmax : Date= minDate(axisMax,addDays(axis.maximum, if(diffDays(newmax,newmin) >= MIN_RANGE) { axis.minimum = newmin; axis.maximum = newmax; _cache.reset(); this.invalidateDisplayList(); } }
-delta)); delta));
private function get axisMin() : Date { return DateTimeAxis(horizontalAxis).parseFunction(series[0].dataProvider[0][seri es[0].xField]); } private function get axisMax() : Date { var dp: ICollectionView = series[0].dataProvider; return DateTimeAxis(horizontalAxis).parseFunction(dp[dp.length1][series[0].xField]); } private static function addDays(date: Date, days:int): Date {
598
RIAWITHADOBEFLEXANDJAVA
DevelopingCustomCharts
}
return new Date(date.getTime()+days*MSEC_IN_DAY);
private static function minDate(date1: Date, date2: Date):Date { return date1.getTime()date2.getTime() ? date1 : date2; }
}
private static function diffDays(date1 : Date, date2: Date): Number { return (getDate(date1).getTime()-getDate(date2).getTime())/MSEC_IN_DAY; private static function getDate(date : Date) : Date { return new Date(date.getFullYear(), date.getMonth(), date.getDate()); } private static function toDateString(date:Date) : String { var d: DateFormatter = new DateFormatter(); d.formatString = “MM/DD/YY JJ:NN:SS”; return d.format(date); } public function get viewBounds() : Rectangle { return super.dataRegion; } protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); _layer.width = width; _layer.height = height; var index: int = _cache.getPointIndexByX(mouseX); _dataTip.visible = index>=0 ; if(_dataTip.visible) { _dataTip.text = getToolTipText(index); _dataTip.width = _dataTip.measuredWidth; _dataTip.height= _dataTip.measuredHeight; _dataTip.x = mouseX+2; _dataTip.y = mouseY-_dataTip.height; RIAWITHADOBEFLEXANDJAVA
599
CHAPTER14
}
}
private function getToolTipText(index: int): String { var sb:String =”” ; for(var i:int=0;i
Listing15.1AnExternalAPIsample
608
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
WestartwithanonCreationCompleteeventhandler.HerewefirstcheckwhethertheExternalAPIis availableand,ifso,registerancallActionScriptinstancemethodclosureunderthename“asFunction”soJavaScriptcodecaninvokethismethodlike: flexObject.asFunction(“String argument”);
NowtakealookatthecallActionScriptmethoditself.Aslongasweuseaninstancemethodclosure, wecanrefertootherinstancevariablesormethods.Intheexampleabovewejustassignthegiven texttotheTextInputcontrol. ThesecondmethodisusedtoinvokeajsFunctionJavaScriptfunctionfromaFlexapplicationinresponsetothebuttonclick.Again,westartwithanExternalAPIavailabilitycheck. Youmaychooseadifferentstrategyforyourapplicationandjustdisablethebuttonafter theinitialcheck.We’repassingseveralparametersofdifferenttypestotheJavaScriptfunction:thenumberofinvocationsasNumber,user-enteredtextasString,currenttimestamp as Date, and even/odd invocation flag as Boolean. Finally, the method handlers return a valuefromtheJavaScriptfunction.IfitreturnsaStringthenweupdatethetextofTextInputcontrol. Next,wehavetocreateanHTMLfilethatembedstheFlexSWF:
Basic External API Example
Send message to Flex
Send
610
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
Listing15.2TheHTMLpageforthesampleExternalAPIapplication Tosavethebookspace,we’veremovedtheboilerplatecodegeneratedbyFlexBuilderthathandles automaticFlashplug-ininstallation,disabledJavaScript,andbrowserhistorynavigation.Sotorun thisexampleyoumusthaveFlashPlayer9installedandJavaScriptenabled. TheHTMLfileembedstheFlexapplicationviaboththeandtagstosupporta widerangeofbrowsersandprovideaminimalsetofcontrolstoinvokeanActionScriptfunctionfrom JavaScript.ThemostinterestingpartisapairofJavaScriptfunctionsdefinedinthe
Listing15.7ThefinalHTMLpagefortheOWCintegrationsample
628
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
Thepageisbeinginitializedintwophases:whenHTMLstartsloadingandafterthecompletepage contentisavailable(seethewindow.onloadeventhandlerinListing15.7).Duringthefirstphase we provide a fallback implementation for both Flex notifications and Spreadhsheet OWC event handlers(packagedasxlOWCEventsobjectmethods).Youmaybesurprisedtoseethatbothgroups ofmethodscontainnorealfunctionality.Let’sassurethereaderthatthecontentofthesemethods isgoingtobedynamicallyreassignedlater.Fornow,wekeepthematbaysothatnoharmisdone iftheyareinvokedintheunsupportedbrowseroratthewrongtime. Tounderstandthekindofproblemswe’retryingtoavoid,imaginethatourHTMLpagewithan OWCexampleisopenedinFirefox.TheSpreadsheetcontrolisunavailableinthisenvironment, buttheFlexapplicationcanrunsuccessfully(aslongastheFlashplug-inisinstalled).Thevery firstnotificationfromFlexwouldcauseaJavaScripterrorifthecorrespondinghandlerattempted toaccessanon-existingcontrol.Sure,it’spossibletoaddthenecessarychecktoeverynotification handler.However,thisiscodeduplication. Thesecondsortofproblemwesolvewith“empty”notificationhandlersaretiming-related.The SWFfileisloadedasynchronouslywiththerestofthepageandthefxMetadatanotificationisfired immediately from the onCreationComplete event handler (see Listing 15.3). Hence, the HTML pagemaygetthisnotificationearlierwhentheSpreadsheetActiveXcontrolisinsertedand/orfully initialized.That’swhynotificationhandlersinthisphasecontainnofunctionalitytoupdatethe SpreadsheetOWC;theyallsettheflagmissedNotificationtorequerynotificationsfromFlexwhen initializationiscomplete. Thesecondphaseofthepageinitializationisexecutedwhenthewindow.onloadeventhappens. ItstartsbylookingforthexlOWCHTMLelementthatweattemptedtodynamicallyinstantiatein Listing15.6.IfnoobjectisreturnedforthisID,there’snosenseintakingfurtheraction–HTMLis runningintheunsupportedenvironment.Otherwiseinitializationcontinuesandthereferenceto thesecondActiveXcontrol,FlashPlayerrunningtheFlexapplication,isstoredaswell. Next,thewindow.onloadhandlerdeclaresseveralmetadata-relatedvariables.Briefly,thesevariablesdescribethestructureoftherecordscomingfromtheFlexapplicationandtheassociated rulestoconvertfieldvaluesbackandforthbetweenFlexandtheSpreadsheet(we’llmakefulluseof thesevariablesinthefxMetadatanotificationhandlerabitlater).Aslongasweneedabidirectional communicationchannel,twomappingstructuresarerequired: •
•
Thenumber-indexedCOLUMNS_XLarraydefinesthemappingbetweentheSpreadsheet columnnumbersandActionScriptfieldnames.Thepositioninthisarraycorrespondstothe indexofthecolumn. TheassociativeCOLUMNS_FXarraydefinesthemappingbetweentheActionScriptfield nameandthecolumnindex.Thekeyinthisarrayisthenameoffield.
ThevariablesCOLUMN_COUNTandROW_COUNTholdthenumberofavailablefields(columns) andrecords(rows)correspondingly.Strictlyspeaking,ROW_COUNTrelatestothedataratherthan tometadata. RIAWITHADOBEFLEXANDJAVA
629
CHAPTER15
Todiscussthenextvariable–xlSheet–weatleastneedarecapoftheMicrosoftExcel/Spreadsheet OWC object model, which we’re going to provide right here in a“Shakespeare on the eggshell” format. The root of the object hierarchy is the Application object. For Spreadsheet OWC it’s the control itself.EveryApplicationcontainsacollectionofWorkbooks,everyWorkbook,inturn,holdsacollectionofWorksheets.Manyobjectsinthehierarchyhaveanotionof“active”sub-elements.Active means“currentlyvisibletotheuserandreadytoaccepttheuser’sinput.”SoanApplicationhasan ActiveWorkbook,andaWorkbookhasanActiveSheet.Transitively,ApplicationhasanActiveSheet propertyaswell. WhenaSpreadsheetcontrolisinitialized,itpre-createsoneactiveWorkbookwiththreeWorksheets; thefirstWorksheetisactive.It’sthereferencetothisveryworksheetthat’sstoredasanxlSheetvariableduringtheinitializationtime. Remember,thereareanumberofdifferencesbetweenexistingOWCversions.Tokeeptrackofthe currentlyusedOWCversionwedefinedthevariablexlVersion.Theversiondifferencescomeinto playimmediately.Infact,everythingwe’vesaidabouttheobjectmodelistruefortheMicrosoftExcelapplicationandSpreadsheetOWCofversions10/11,butdoesn’tstandforSpreadsheetversion 9,whichusesacompletelydifferentAPI.TherearenosuchthingslikeWorkbook,Worksheet,and thecollectionsthereofinOWC9.TheSpreadsheetisamixofApplicationwithasoleactiveWorkbook that has one and only one (active)Worksheet. However, the combination of the Microsoft COMlate-bindingmechanism,theJavaScript“ducktyping,”andthesimilaritiesbetweenSpreadsheetandWorksheetAPIsletustreatSpreadsheetasaWorksheetincertaincases:atleastOWC9 functionalityisenoughtoimplementFlex-to-Spreadsheetcommunicationsfully;whenitcomes toanythingbesidethis,ourscriptdegradesgracefully. XLConstantsprovidesashortcuttoallconstantsusedintheSpreadsheetOWCmethods.InscriptinglanguageslikeJScriptorVBScriptthere’snowaytorefertheconstantsdeclaredinActiveX-type librariesbyname.Hence,everyOWCcontrolexposesasetofconstantsasitsowncomplexproperty. Just before returning from the window.onload event handler, when all setup and preparation is done,acheckforlostnotificationsfromtheFlexapplicationisdone.Ifthereareany,theFlexapplicationisaskedtoresendnotificationsforbothmetadataandavailabledataentries(seefxGrid. xlResend()invocation).
HandlingFlexMetadataNotification Asareminder,oneofourprimarydesigngoalsistomaketheJavaScriptcodegenericandreusable whilefullyusingExcelformatingcapabilities.Tothatextentweexternalizethemetadata.Thispart ofthecode,whichalsofulfilsourpromiseofoverwritingtheFlexmetadatanotificationfunction, ispresentedinListing15.8:
630
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications function setupFxMetadata() { function AS_IS(v) { return v; } function variantDate2jsDate (v) { return null == v ? null : new Date(v); XL_FORMATTERS = { date: variantDate2jsDate, time: variantDate2jsDate, dateTime: variantDate2jsDate, boolean: function(v) { return v == null ? null : !!v; }, string: AS_IS, number: AS_IS };
}
function fmtDate(v) { return [v.getFullYear(), v.getMonth() + 1, v.getDate()]. join(“-”); } function fmtTime(v) { return [v.getHours(), v.getMinutes(), v.getSeconds()]. join(“:”); } FX_FORMATTERS = { date: fmtDate, time: fmtTime, dateTime: function(v) { return fmtDate(v) + “ “ + fmtTime(v); }, boolean: AS_IS, string: AS_IS, number: AS_IS }; fxMetadata = function(meta) { meta = meta || []; ROW_COUNT = 0; fxRowsUpdated(0, [], true); COLUMNS_FX = {}; COLUMNS_COUNT = meta.length; COLUMNS_XL = new Array(COLUMNS_COUNT); var xlHeadings; if (xlVersion >= 10) { var xlWnd = xlOWC.activeWindow; xlWnd.resetHeadings(); xlHeadings = xlWnd.columnHeadings; } else { var FAKE_HEADING = {}; xlHeadings = function(idx) { return FAKE_HEADING; } } for (var i = 0; i < COLUMNS_COUNT; i++) { var field = meta[i]; COLUMNS_XL[i] = field.name; field.idx = i + 1; field.xl = XL_FORMATTERS[field.type]; RIAWITHADOBEFLEXANDJAVA
631
CHAPTER15
}
}
}
field.fx = FX_FORMATTERS[field.type]; COLUMNS_FX[field.name] = field; xlHeadings(i + 1).caption = field.title;
Listing15.8Initializingmetadatafunctions WhenourJavaScriptisnotifiedofmetadatachanges(thefunctionfxMetadata),itmustdoseveral things.First,it’snecessarytoresetallexistingvariablesthatarerelatedtothemetadata.Wealso needtodroptheexistingcontent.Otherwisetheuserwillbeconfusedtoseealogicalmismatch betweencolumnheadingsandcellcontent.Afterwards,thehandlerre-createsJavaScriptmetadata variablesaccordingtoparametersfromActionScript. WeusetheactiveWindowinstancetooverwriteadefaulttextofcolumnheadingswithuser-friendlyfieldtitles(comparetheSpreadsheetcolumnheadingtextsinFigures15.2and15.3).Notethat thoughtheWindowobjectispresentinbothExcelandSpreadsheetOWC10/11,thecolumn/row headingsfunctionalityisuniquetotheSpreadsheetcomponent.Asyoucanseefromtheworkaroundapplied,SpreadsheetOWC9hasnosuchfunctionality. TheremainingpartofthecodeinListing15.8dealswithtypeformats.Excelissmartenoughto auto-formatacellbasedonthevaluesentered.SoifwetransformtheoriginalvaluetotheExcelfriendlyform,thenecessaryformattingwillbeappliedautomatically. Be aware that the primary scripting language of Microsoft Office Applications isVisual Basic. It couldeitherbeVBAinsideOfficeapplicationsorVBScriptforOfficeWebComponents.Infact,the veryfirstVisualBasiccustomerwastheMicrosoftExcelteam! ManyVisualBasicfeaturesmaylookstrangefromtheJavaScriptperspective.Oneofthem,forexample,isthatindexinginVisualBasicstartsfrom1(one)whereasinJavaScriptorActionScriptit startsfrom0(zero).Sobepreparedtosee“row+1”statementshereandthereinexamplecode. ThesecondissueisthatthepresentationofcertaintypesdiffersbetweenVisualBasicandJScript. ThemostproblematiconeisDate.VisualBasicacceptsadateasavariantoftheVT_DATEtype.Underthehood,it’safloating-pointvalue,countingdayssincemidnightDecember30,1899.Hours andminutesarerepresentedasfractionaldays.IntheECMAScriptfamilyoflanguageslikeJavaScript,ActionScript,orJScript,adateisrepresentedasanobjectoftheDatetypeanditsinternal representationisthenumberofmillisecondssinceJanuary1,1970.There’senoughdifferencesto getupset! That said, Excel is smart enough to parse the date from a string in several formats3, for example, “2006-06-0414:30:30”isrecognizableasadate/timevalue.ThesetofFX_FORMATTERSfordate/ timetypesusesthisfeature.ThevaluesofotherJavaScripttypescanbesafelypassedtoExcelasis.
632
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
Whilemovingthedataintheoppositedirection,weneedtoconvertVT_DATEtoJavaScript.Thankfully,it’sjustaDateconstructorcallwithVT_DATEasanargument(seethevariantDate2jsDate functioninListing15.8),aneatundocumentedfeatureofJScript.ForfieldsdefinedasBooleanwe needtoensurethatthevalueenteredintheSpreadsheetisindeedBoolean.Tothatendweapply thegoodoldJavaScript“negationofnegation”idiomtoconvertthevalueenteredtotheBoolean type: var numberF = 0, numberT = 255; var stringF = “”, stringT = “ABC”; var objectF = null, objectT = new Date(); var var var var var var
isNumberF isNumberT isStringF isStringT isObjectF isObjectT
= = = = = =
!!numberF; !!numberT; !!stringF; !!stringT; !!objectF; !!objectT;
// // // // // //
false; true; false; true; false; true;
YoumayhavenoticedthatweareupdatingthemetadatafieldobjectsthatcomefromActionScript. Suchupdatesdon’taffectoriginalobjectsinActionScriptduetothemarshalingusedbytheExternalAPIprotocol(we’lldiscussthislater).
SynchronizingSpreadsheetOWCwithFlexDataChanges TodiscussthepartofthecodethathandlesdatachangenotificationscomingfromtheFlexapplication,wehavetodigressaleveldeeperintheMicrosoftExcel/SpreadsheetOWCobjectstree.The codeoperatesonthecontentoftheWorksheet,whichismadeupofcells.There’snoCellobjectin thetreeperse,butthere’saRangeobject.Rangeisalightweightobjectthatprovidesaccesstoany numberofcellsdependingontherangebounds.SoyoucanthinkofacellasjustaRangewiththe top-leftcornerequaltothebottom-rightone.Similarly,acolumnisaRangewiththeleftbound equal to the right bound, the top bound is equal to one, and the bottom bound is equal to the maximumnumberofrowsallowed(inrecentExcelversionsit’s2^16).Bythesameanalogy,arow isaRangewiththetopboundequaltothebottombound,theleftequaltooneandtherightequal tothemaximumnumberofcolumnsallowed(upto2^8). ThenumberofmethodstogettheRangeobjectintheExcel/SpreadsheetOWCfaroutweighsthe numberofwaystoskinacat.Dependingonyourneeds,it’spossibletoconstructaRangefroma Worksheetsupplyingthepairoftop/leftandbottom/rightcells;it’spossibletogetaRangebythe nameofthecells(forexample,“A1:B2”);there’sanotionofanactivecellandcurrentselection;the resultoftheintersection/unionoperationsonrangesis,obviously,Rangeaswell.Foracomplete listseetheExcelVBAdocumentationavailablewiththeOfficeWebComponents. ARangeisaveryefficientabstractionwhenitcomestodataoperationsorformatting.Ifyoureada valuefromtheRange,thevaluereturnedistakenfromthetop-levelcell.WhenyouassignanindiRIAWITHADOBEFLEXANDJAVA
633
CHAPTER15
vidualvaluetotheRangeormodifyitsvisualpropertylikeFillStyleitaffectsallcellsintheRange. The code we’re going to present in Listing 15.9 reacts to data changes on the Flex side and modifiestheSpreadsheetaccordingly.Inessence,itsequentiallyassignsvaluestoindividual cells.This approach has a serious associated performance penalty.The cost of multiple assignmentsandingeneralthecostofmultiplecallsacrossCOMobjectboundariesisinherentlyhigh.Tomakethingsworse,withJScriptandVBScriptwe’relimitedtotheCOMIDispatch interface. We’lladdresstheperformanceimprovementslater,wheninsteadofindividualvalueassignments we’lluseanotherfeatureofRange–thesupportoftabulardataassignmentssuchaspastingHTML contentfromtheclipboardorcopyingvaluesfromADODB.Recordset.InthesecasesRangeforces datatobesplitupintoindividualvalues,onepercell. function setupFxDataNotifications() { function xlCopyCells(offset, content) { for (var i = content.length - 1; i >= 0; i--) { var obj = content[i]; var row = i + 1 + offset; for (var name in COLUMNS_FX) { var col = COLUMNS_FX[name]; xlSheet.cells(row, col.idx).value = col.fx(obj[name]); } } }
}
function xlTransaction(fn) { xlOWC.enableEvents = false; xlOWC.beginUndo(); try { fn(); } finally { xlOWC.endUndo(); xlOWC.enableEvents = true; }
fxRowsUpdated = function(offset, content, reset) { xlTransaction(function() { var cells = xlSheet.cells; if (reset) { cells.clear(); offset = 0; } xlCopyCells(offset, content); var count = content.length; if (reset) ROW_COUNT = count;
634
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications if ( !count ) return;
};
var range = xlSheet.range( xlSheet.cells(offset + 1, 1), xlSheet.cells(offset + count, COLUMNS_COUNT) ); range.select(); if (reset && xlVersion >= 10) range.columns.autoFit(); });
fxRowsInserted = function(offset, content) { var count = content.length; if (!count) return; xlTransaction(function(){ var row = offset + 2; while (count--) xlSheet.rows(row).insert(); xlCopyCells(offset + 1, content); var range = xlSheet.range( xlSheet.cells(row, 1), xlSheet.cells(row + content.length - 1, COLUMNS_COUNT) ); range.select(); }); };
}
fxRowsRemoved = function(offset, count) { if (!count) return; xlTransaction(function(){ var row = offset + 1; while (count--) xlSheet.rows(row)[“delete”](); var range = xlSheet.range( xlSheet.cells(row, 1), xlSheet.cells(row, COLUMNS_COUNT) ); range.select(); }); };
Listing15.9HandlingdatachangesinaFlexapplication MethodsxlCopyCellsandxlTransactionareusedbyFlexdatachangenotificationhandlers.TherationalebehindxlCopyCellsisclear.Givenazero-basedoffsetandcontentasanarrayofobjects,the RIAWITHADOBEFLEXANDJAVA
635
CHAPTER15
methodassignsavalueofeverypropertytocorrespondingcells.ThisiswheretheFX_COLUMNS associativearraycomesinhandy.Itprovidesawaytobothorderpropertiesandgetcorrectformatingfunctiontoconvertvaluesasnecessary. ThexlTransactionmethodisapurefunctiondecorator.Firstofall,itdisablesnotificationevents fromSpreadsheetOWCwhenwe’remodifyingcells.Otherwise,there’sariskofeithergettinginto infiniterecursionorissuinganunnecessaryupdatetotheFlexapplication.Second,itletsusenlist asetofoperationsforasingleundo“command,”orotherwiseusersmightbesurprisedwiththe numberofclicksneededtoundothechangesontheSpreadsheetsideofasingleatomicrecord insertion/modification/deletiondoneonthesideoftheFlexapplication. Onasidenote,beginUndo/endUndofunctionalityisn’tuniquetoSpreadsheetOWC.SimilarfunctionalityexistsinExcel.Butit’simplementedinadifferentwayastheApplication.OnUndo(“MenuText,” “ProcedureName”)method. The Flex notification handlers fxRowsUpdated, fxRowsInserted, and fxRowsRemoved are quite easy to follow. All of them pass processing blocks as function closures to xlTransaction. For the insertion/deletion of rows we first get the complete row from an existing Range via the xlSheet. rows(idx)call.Bothinsertanddeletemethodshaveoptionalparametersthatdefinehowtoshift theremainingcells.However,ifRangeisacompletecolumn/rowwecansafelyskipthisparameter. Note how we use“key notation” inside fxRowsRemoved to avoid using dot notation against the delete,whichisaJavaScriptkeyword. Sofarwe’veenabledtheSpreadsheetpopulationwithdatafromtheFlexapplication.Userscan editdatainFlexandchangesareautomaticallypropagatedtoSpreadsheet. Inthenextsectionwe’lllookatSpreadsheet-to-Flexdataupdates.
SynchronizingFlexwithSpreadsheetDataChanges ThecodethatwillsenddatachangedinSpreadsheetbacktoFlexispresentedinListing15.10:4 function setupXlDataNotifications() { xlOWCEvents.sheetChange = function(sheet, range) { if (!ROW_COUNT || (xlSheet != sheet)) return; xlOWC.enableEvents = false; try { var r1 = range.row; var c1 = range.column; var rc = range.rows.count; var cc = range.columns.count; if (r1 > ROW_COUNT || c1 > COLUMNS_COUNT) return; var rows = Math.min(rc, ROW_COUNT - r1 + 1);
636
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications var cols = Math.min(cc, COLUMNS_COUNT - c1 + 1); var content = new Array(rows); for (var i = 0; i < rows ; i++) { var row = content[i] = {} for (var j = 0, col = c1 - 1; j < cols; j++, col++) { var prop = COLUMNS_XL[col]; row[prop] = COLUMNS_FX[prop].xl( range.cells(i+1, j+1).value ); } } fxGrid.xlImportRows(r1 - 1, content, false)
}
};
} finally {
xlOWC.enableEvents = true;
}
Listing15.10HandlingdatachangesinSpreadsheetOWC The xlOWCEvents.sheetChange method gets two parameters from the corresponding event: the referencetotheWorksheetwheretheeventoccurredandtherangeofaffectedcells.Potentially, thisrangecanbelargeenoughtofreezetheprogramforever.ImagineausecasewhereauserselectsseveralcolumnsandpressestheDeletekey.TheRangeparameterforsuchaneventcontains 2^16rows.Clearly,processingallofthemwouldtakeanunacceptableamountoftime.Sothefirst thingtheeventhandlerabovedoesisrestricttheprocessingareatosomereasonableregion.In ourcaseit’sanintersectionoftheparameterrangewith(1,1):(ROW_COUNT,COLUMN_COUNT) area.AftergettingcorrectboundariestheonlythingleftistoconstructthepayloadfortheFlex xlImportRowsmethod. Let’sstressthattheabovemethodsupportsfine-grainedchangenotifications.Iftheuserupdates avalueonlyinonecell,thenexactlyonepropertyofasingleobjectwillbeupdated.Iftheuser selectsarangeofcellsandupdatesthematonce(asaruleviacopyingclipboarddataordeleting values), then the update package will contain only the rows affected with every row containing onlyaffectedproperties.ThexlImportRowsmethodontheFlexsideknowsaboutthisfeatureand behavesaccordingly. ThedefinitionofthexlOWCEvents.sheetChangemethodonitsown,however,isn’tsufficient.This iswherethefollowingstrangescriptblockcomesintoplay:
ThisisanMSIE-specificwaytoattacheventhandlers.AscriptblockmustcontainaFORattribute thatspecifiesanIDofthesourceHTMLelementandEVENTattributethatdescribesaneventsignature.Variablenamesusedinsideaneventsignaturemaybeusedinascriptblocktoaccessevent RIAWITHADOBEFLEXANDJAVA
637
CHAPTER15
parameters.Ifthebrowsercan’tfindanHTMLcontrolwiththeIDprovided,oracontroldoesn’t exposetheeventwiththenamespecifiedinthesignature,thenthescriptblockissimplyignored. SoaSpreadsheetOWC9thathasnoSheetChangeeventwillbesafelytakenoutofthegamewith noadditionalcoding. It’sworthmentioningthatcertainbrowsershavenocluewhatFORandEVENTattributesarefor (nopunindeed).Butoncetheyseeascriptblock,theyjustexecuteit.Doyourecallthatinitially (seeListing15.7)wecreatedxlOWCEventswith“empty”methods?Nowyouknowwhatwediditfor –topreventusersofnon-MicrosoftbrowsersfromJavaScripterrorsduringpageloading. Another important thing: a separate FOR/EVENT script block is the only valid way to assign an eventhandlertoanActiveXcontrol.IfyoutrytodothesamethingviaaDOMLevel0event-handlingmechanismlikeayofthosebelow: xlOWC.SheetChange = xlOWC.sheetChange = xlOWC.sheetchange = xlOWC.onsheetchange xlOWC.onSheetChange
function() {...}; function() {...}; function() {...}; = function() {...}; = function() {...};
youendupwithanewpropertyoftheFunctiontypeinanOBJECTHTMLelement(aso-called “expando”property)insteadofaneventhandler.IfyoutryanMSIEvariationoftheDOMLevel2 mechanism: xlOWC.attachEvent( “SheetChange”,
function() { ... } )
you can attach an event handler, but it will cause Spreadsheet OWC/Internet Explorer to crash soonerorlater.TrytoattachanemptyeventhandlertotheSheetChangeeventthiswayandedit 8-15cells.You’llbeforwardedtoMicrosoft’ssupportcenterbyagentlequalityfeedbackagent.It’s hardtosayforsure,butattachEventseemstohaveissueswithcertainActiveXthreadingmodels. TheremainingblockenhancesourexamplewithacustomSpreadsheetmenu,theabilitytosave Spreadsheetcontenttothelocalfilesystem,andseveralothermiscellaneousfunctions: function setupXlContextMenu() { function copy_vb_array(variant) { var vba = new VBArray(variant); var low = vba.lbound(1), high = vba.ubound(1); var res = new Array(high - low + 1); for (var i = low, idx = 0; i = 11 ? ctxMenu : clone_array(ctxMenu);
xlOWCEvents.commandBeforeExecute = function(cmd, cancel) {
640
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
}
};
var handler = customCommands[cmd]; if ( !handler ) return; /* Disable default processing of command */ cancel.value = true; handler();
Listing15.11AcustomcontextmenuforSpreadsheetOWC Toaddourowncontextmenucommandsweneedtohandleatleasttwoevents:BeforeContextMenuandCommandBeforeExecute.BeforeContextMenualtersorreplacesthedefaultmenusuppliedbyaparameter,whileCommandBeforeExecutewillhelpusperformaconcreteactionwhen theuserselectsthemenuitem. NotethatthemenuparameterofBeforeContextMenucomesasaspecialByReftypeintroduced bySpreadsheetOWCtosupportin/outparameters(unlikeVisualBasicneithertheJScriptnorVBScript support parameter is passed by reference).Variables of ByRef type have a single property –value. Alteringthecontextmenuisreallyatrickypart.Whenyoureadthemenu’svalueyougetaVisual Basic Array (VT_ARRAY). Every item of this array is either a [Text, CommandIdentifier] pair or a [Text,Submenu]pairtoaccommodatethearbitrarynestingofsubmenus.Inasenseyouaregetting anarrayofarrays.Beforeyoucandoanythingelseyouhavetoconverttheoriginalmenutothe JavaScriptArray–seetherecursivecopy_vb_arrayfunctionthatdoesthetrick.Handlingrecursion hereisquiteeasygiventhattheJavaScripttypeoffairlyreports“unknown”(andundocumented, onemightadd)forVT_ARRAY. NextwecanextendthemenuwithitsownitemsusingconvenientJavaScriptArraymethodslike spliceandpush.Therearetwooptionsforcommandidentifiers:integercodesandstrings.Integer codesarereservedbytheSpreadsheetOWCsoyoucanonlyusetheonesdefinedinXLConstants tooverridethebehavioroftheexistingcommandsoraddexistingcommandsifthey’renotavailablebydefault.Stringidentifiersareatyourdisposaltoimplementcustomcommands.Toinsert separator,usenullasthemenuitem. Rememberweconvertedmenu’svaluefromtheVBarraytotheJavaScriptarrayformodifications? Funny enough, there’s no need to convert the modified array back to aVB array before assigning it to a menu.value.There’s another“subtle” problem specific to OWC 10. If a corresponding JavaScriptarraylengthhasbeenchangedaftercreation(asaresultofassigninganelementwith theindexhigherthenorequaltothelength,invokingpushorspliceonanarray,etc.),themenuis reportedas“invalid!”Theonlyworkaroundistodeepclonethealready-builtarray,supplyingthe exactlengthtotheconstructorateverystep. ThefunctionalityofthexlOWCEvents.commandBeforeExecuteissimpleenough.ItattemptsalookRIAWITHADOBEFLEXANDJAVA
641
CHAPTER15
upviatheregistryofcustomcommandsandexecutesone,iffound.Inthiscasewehavetocancel thedefaultmenuprocessing. Therearethreecommandsofparticularinterest(seeobjectcustomCommandsinListing15.11). Twoofthem–cmdSaveXlsandcmdSaveHtml–savethecontentoftheSpreadsheetOWCComponentinacorrespondingExcelXMLandHTMLformat.Savingafiletothelocalfilesystemissubject tosecurityrestrictions.Sobeprepared.Thesefunctionsmayormaynotworkonaparticularclient computerdependingonitssecuritysettings. Thethirdcommand,cmdSendData,sendsacompletedatasetbacktotheFlexapplication.Inthe currentimplementationthisistheonlywaytoaddrowsdirectlyintheSpreadsheetwhilethexlOWCEvents.sheetChangeeventhandlerignoresanysheetcontentbeyondROW_COUNT.ThexlSheet. cells(1,1).currentRegionletsusgetacontinuousregionfromthefirstcelltoanythingboundedby anemptyrowandemptycolumn.ValuesfromthisregionarepackedasparameterstofxGrid.xlImportRows()thesamewayitwasdoneinxlOWCEvent.sheetChange()(seeListing15.10). Notethatwedon’tdisableSpreadsheeteventswhenexecuting“Clear”commandsunlikethexlTransactionforFlexdatanotifications(seeListing15.9).NowwhenaSheetChangeeventisfiredas aresultofa“Clear”commandexecution,theFlexapplicationcontentwillbeclearedaswell.
642
Figure15.4ThecustomcontextmenuforSpreadsheetOWC
RIAWITHADOBEFLEXANDJAVA
IntegrationwithExternalApplications
We’redone.LookingbackatListings15.3and15.7through15.11,youmayhavethefeelingthatthis wasallrathercomplex.However,thecomplexityisentirelyassociatedwiththeenormousSpreadsheetOWCAPI,VB/JScripttypeconversions,plainOWCbugs,andundocumentedJScriptfeatures. Onthebrightside,allthecoderelatedtotheFlexExternalAPIwasstraightforwardandeasyto follow. Whilebeingcomplex,OWCSpreadsheet/FlexintegrationpresentsaniceoptionforhybridHTML/ Flexsolutions.Onthisnotewe’dliketomentionanotherOWCcontrol–PivotTable,whichoffers functionalityunmatchedinFlex.IntegrationwithPivotTable,however,isbeyondthescopeofthis chapter.
One-WayFlex-ExcelIntegrationviatheSystemClipboard Sofarwehaven’tmentionedthesystemclipboard–asolutionthatletsFlexexchangedatawithnativeapplications.FlexletsyouputtextdataonthesystemclipboardviatheSystem.setClipboard() method.(UnliketypicalnativeclipboardAPIsFlexsupportsonlyoneclipboardformat–text.Anotherlimitation,duetosecurityconsiderations,isthatyoucan’tgettheclipboardcontents.) Listing15.12presentsaslightlymodifiedversionoftheFlexapplicationusedinourpreviousexample.WeremovedcoderelatedtoExternalnotificationsand,forsimplicity,droppedthefxMetadata()functioninfavorofastaticconstantarrayormetadata.Whatwe’veaddedhereisa“Copyto clipboard”buttonwiththeactionhandler: