You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ApplicationConnection.java 143KB

Font icon support (#13152) Renamed Icon to ImageIcon Change-Id: I608815f17a3651b205fed81b5294385df0d68802 Extracted the abstract client-side Icon class Change-Id: Ic32e270595a5796d0bbd1dd31f34282b56672aa9 Created the FontIcon class Change-Id: Iad13871e7bf1807dee2c538c76306d4620191f5e Renamed AbstractComponentConnector.getIcon to getIconUri Change-Id: I6953ab79661993b561655d483c1bd013b66407f3 Added the AbstractComponentConnector.getIcon method Change-Id: I6fb91dc643fb09da3ba53666b1a8a289901702e3 Refactored getIcon Change-Id: Ibae39e66d0fb8449e20ac5209eb8c18b6ada4387 Made all existing uses of Icon compatible with FontIcons Change-Id: I8f28ec5254f2e5282a887519d3f44bc1e27aba72 Initial server-side support for font icons - does not include an actual icon set yet (#13152) Change-Id: Ie6c09b17dd577c726e0efc13567749f6f4d56d8d Changed server side FontIcon URI generation to match the correct scheme Change-Id: I3628b930b310b3f285bc58a3f471e31e641d307e Initial server-side icon font (FontAwesome) with scss - to be considered placeholder for testing (#13152) Change-Id: I361e62aba0d943a736471824e149d65c7eea9c76 Changed the FontIcon URI scheme Change-Id: I15c92f6bb3d0aa0a800f3f0bfa80419979453e17 Added FontIcon support to AbstractOrderedLayoutConnector Change-Id: I3b2b45b22d29622fd888dbe922aa0cc8a718104d Added FontIcon support to table items Change-Id: Id22ce94c96a892420aab1e39663688fc9f3bc282 Added FontIcon support to OptionGroup items Change-Id: Ie08bef688f6802182ef5f8b2bf82cf8b1f9096bb Switched to openly use FontAwesome (#13152) Change-Id: I18c3325ce93915b7fd6e338c8c293a89711277bc VaadinIcons are now FontAwesome (#13152) Change-Id: I0ab2a80735cbf08b6e33d358e3e8c6a205626fc4 VCaption does not longer set icon to 0x0px if it's a FontIcon (#13152) Change-Id: Ibcd96e0f79f0adf2e217a8580d17f1cc93705710 Fixed typo in @font-face, removed .otf (#13152) Change-Id: I698ca32c560e5f198c32a6c44f7884d3030ee610 Make font icons behave more like img (display:inline-block) (#13152) Change-Id: Ic79186c90f1fc566deae1f4d8d4ba2c21d89a42e
преди 10 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
преди 12 години
Font icon support (#13152) Renamed Icon to ImageIcon Change-Id: I608815f17a3651b205fed81b5294385df0d68802 Extracted the abstract client-side Icon class Change-Id: Ic32e270595a5796d0bbd1dd31f34282b56672aa9 Created the FontIcon class Change-Id: Iad13871e7bf1807dee2c538c76306d4620191f5e Renamed AbstractComponentConnector.getIcon to getIconUri Change-Id: I6953ab79661993b561655d483c1bd013b66407f3 Added the AbstractComponentConnector.getIcon method Change-Id: I6fb91dc643fb09da3ba53666b1a8a289901702e3 Refactored getIcon Change-Id: Ibae39e66d0fb8449e20ac5209eb8c18b6ada4387 Made all existing uses of Icon compatible with FontIcons Change-Id: I8f28ec5254f2e5282a887519d3f44bc1e27aba72 Initial server-side support for font icons - does not include an actual icon set yet (#13152) Change-Id: Ie6c09b17dd577c726e0efc13567749f6f4d56d8d Changed server side FontIcon URI generation to match the correct scheme Change-Id: I3628b930b310b3f285bc58a3f471e31e641d307e Initial server-side icon font (FontAwesome) with scss - to be considered placeholder for testing (#13152) Change-Id: I361e62aba0d943a736471824e149d65c7eea9c76 Changed the FontIcon URI scheme Change-Id: I15c92f6bb3d0aa0a800f3f0bfa80419979453e17 Added FontIcon support to AbstractOrderedLayoutConnector Change-Id: I3b2b45b22d29622fd888dbe922aa0cc8a718104d Added FontIcon support to table items Change-Id: Id22ce94c96a892420aab1e39663688fc9f3bc282 Added FontIcon support to OptionGroup items Change-Id: Ie08bef688f6802182ef5f8b2bf82cf8b1f9096bb Switched to openly use FontAwesome (#13152) Change-Id: I18c3325ce93915b7fd6e338c8c293a89711277bc VaadinIcons are now FontAwesome (#13152) Change-Id: I0ab2a80735cbf08b6e33d358e3e8c6a205626fc4 VCaption does not longer set icon to 0x0px if it's a FontIcon (#13152) Change-Id: Ibcd96e0f79f0adf2e217a8580d17f1cc93705710 Fixed typo in @font-face, removed .otf (#13152) Change-Id: I698ca32c560e5f198c32a6c44f7884d3030ee610 Make font icons behave more like img (display:inline-block) (#13152) Change-Id: Ic79186c90f1fc566deae1f4d8d4ba2c21d89a42e
преди 10 години
Font icon support (#13152) Renamed Icon to ImageIcon Change-Id: I608815f17a3651b205fed81b5294385df0d68802 Extracted the abstract client-side Icon class Change-Id: Ic32e270595a5796d0bbd1dd31f34282b56672aa9 Created the FontIcon class Change-Id: Iad13871e7bf1807dee2c538c76306d4620191f5e Renamed AbstractComponentConnector.getIcon to getIconUri Change-Id: I6953ab79661993b561655d483c1bd013b66407f3 Added the AbstractComponentConnector.getIcon method Change-Id: I6fb91dc643fb09da3ba53666b1a8a289901702e3 Refactored getIcon Change-Id: Ibae39e66d0fb8449e20ac5209eb8c18b6ada4387 Made all existing uses of Icon compatible with FontIcons Change-Id: I8f28ec5254f2e5282a887519d3f44bc1e27aba72 Initial server-side support for font icons - does not include an actual icon set yet (#13152) Change-Id: Ie6c09b17dd577c726e0efc13567749f6f4d56d8d Changed server side FontIcon URI generation to match the correct scheme Change-Id: I3628b930b310b3f285bc58a3f471e31e641d307e Initial server-side icon font (FontAwesome) with scss - to be considered placeholder for testing (#13152) Change-Id: I361e62aba0d943a736471824e149d65c7eea9c76 Changed the FontIcon URI scheme Change-Id: I15c92f6bb3d0aa0a800f3f0bfa80419979453e17 Added FontIcon support to AbstractOrderedLayoutConnector Change-Id: I3b2b45b22d29622fd888dbe922aa0cc8a718104d Added FontIcon support to table items Change-Id: Id22ce94c96a892420aab1e39663688fc9f3bc282 Added FontIcon support to OptionGroup items Change-Id: Ie08bef688f6802182ef5f8b2bf82cf8b1f9096bb Switched to openly use FontAwesome (#13152) Change-Id: I18c3325ce93915b7fd6e338c8c293a89711277bc VaadinIcons are now FontAwesome (#13152) Change-Id: I0ab2a80735cbf08b6e33d358e3e8c6a205626fc4 VCaption does not longer set icon to 0x0px if it's a FontIcon (#13152) Change-Id: Ibcd96e0f79f0adf2e217a8580d17f1cc93705710 Fixed typo in @font-face, removed .otf (#13152) Change-Id: I698ca32c560e5f198c32a6c44f7884d3030ee610 Make font icons behave more like img (display:inline-block) (#13152) Change-Id: Ic79186c90f1fc566deae1f4d8d4ba2c21d89a42e
преди 10 години
преди 12 години
преди 12 години
преди 12 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.Date;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import java.util.logging.Logger;
  28. import com.google.gwt.aria.client.LiveValue;
  29. import com.google.gwt.aria.client.RelevantValue;
  30. import com.google.gwt.aria.client.Roles;
  31. import com.google.gwt.core.client.Duration;
  32. import com.google.gwt.core.client.GWT;
  33. import com.google.gwt.core.client.JavaScriptObject;
  34. import com.google.gwt.core.client.JsArray;
  35. import com.google.gwt.core.client.JsArrayString;
  36. import com.google.gwt.core.client.Scheduler;
  37. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  38. import com.google.gwt.dom.client.Element;
  39. import com.google.gwt.event.shared.EventBus;
  40. import com.google.gwt.event.shared.EventHandler;
  41. import com.google.gwt.event.shared.GwtEvent;
  42. import com.google.gwt.event.shared.HandlerRegistration;
  43. import com.google.gwt.event.shared.HasHandlers;
  44. import com.google.gwt.event.shared.SimpleEventBus;
  45. import com.google.gwt.http.client.Request;
  46. import com.google.gwt.http.client.RequestBuilder;
  47. import com.google.gwt.http.client.RequestCallback;
  48. import com.google.gwt.http.client.RequestException;
  49. import com.google.gwt.http.client.Response;
  50. import com.google.gwt.http.client.URL;
  51. import com.google.gwt.json.client.JSONArray;
  52. import com.google.gwt.json.client.JSONNumber;
  53. import com.google.gwt.json.client.JSONObject;
  54. import com.google.gwt.json.client.JSONString;
  55. import com.google.gwt.regexp.shared.MatchResult;
  56. import com.google.gwt.regexp.shared.RegExp;
  57. import com.google.gwt.user.client.Command;
  58. import com.google.gwt.user.client.DOM;
  59. import com.google.gwt.user.client.Timer;
  60. import com.google.gwt.user.client.Window;
  61. import com.google.gwt.user.client.Window.ClosingEvent;
  62. import com.google.gwt.user.client.Window.ClosingHandler;
  63. import com.google.gwt.user.client.ui.HasWidgets;
  64. import com.google.gwt.user.client.ui.Widget;
  65. import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
  66. import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
  67. import com.vaadin.client.ResourceLoader.ResourceLoadListener;
  68. import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
  69. import com.vaadin.client.communication.Heartbeat;
  70. import com.vaadin.client.communication.JavaScriptMethodInvocation;
  71. import com.vaadin.client.communication.JsonDecoder;
  72. import com.vaadin.client.communication.JsonEncoder;
  73. import com.vaadin.client.communication.PushConnection;
  74. import com.vaadin.client.communication.RpcManager;
  75. import com.vaadin.client.communication.StateChangeEvent;
  76. import com.vaadin.client.componentlocator.ComponentLocator;
  77. import com.vaadin.client.extensions.AbstractExtensionConnector;
  78. import com.vaadin.client.metadata.ConnectorBundleLoader;
  79. import com.vaadin.client.metadata.Method;
  80. import com.vaadin.client.metadata.NoDataException;
  81. import com.vaadin.client.metadata.Property;
  82. import com.vaadin.client.metadata.Type;
  83. import com.vaadin.client.metadata.TypeData;
  84. import com.vaadin.client.ui.AbstractComponentConnector;
  85. import com.vaadin.client.ui.AbstractConnector;
  86. import com.vaadin.client.ui.FontIcon;
  87. import com.vaadin.client.ui.Icon;
  88. import com.vaadin.client.ui.ImageIcon;
  89. import com.vaadin.client.ui.VContextMenu;
  90. import com.vaadin.client.ui.VNotification;
  91. import com.vaadin.client.ui.VNotification.HideEvent;
  92. import com.vaadin.client.ui.VOverlay;
  93. import com.vaadin.client.ui.dd.VDragAndDropManager;
  94. import com.vaadin.client.ui.ui.UIConnector;
  95. import com.vaadin.client.ui.window.WindowConnector;
  96. import com.vaadin.shared.AbstractComponentState;
  97. import com.vaadin.shared.ApplicationConstants;
  98. import com.vaadin.shared.JsonConstants;
  99. import com.vaadin.shared.Version;
  100. import com.vaadin.shared.annotations.NoLayout;
  101. import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
  102. import com.vaadin.shared.communication.MethodInvocation;
  103. import com.vaadin.shared.communication.SharedState;
  104. import com.vaadin.shared.ui.ui.UIConstants;
  105. import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
  106. /**
  107. * This is the client side communication "engine", managing client-server
  108. * communication with its server side counterpart
  109. * com.vaadin.server.VaadinService.
  110. *
  111. * Client-side connectors receive updates from the corresponding server-side
  112. * connector (typically component) as state updates or RPC calls. The connector
  113. * has the possibility to communicate back with its server side counter part
  114. * through RPC calls.
  115. *
  116. * TODO document better
  117. *
  118. * Entry point classes (widgetsets) define <code>onModuleLoad()</code>.
  119. */
  120. public class ApplicationConnection implements HasHandlers {
  121. /**
  122. * Helper used to return two values when updating the connector hierarchy.
  123. */
  124. private static class ConnectorHierarchyUpdateResult {
  125. /**
  126. * Needed at a later point when the created events are fired
  127. */
  128. private JsArrayObject<ConnectorHierarchyChangeEvent> events = JavaScriptObject
  129. .createArray().cast();
  130. /**
  131. * Needed to know where captions might need to get updated
  132. */
  133. private FastStringSet parentChangedIds = FastStringSet.create();
  134. }
  135. public static final String MODIFIED_CLASSNAME = "v-modified";
  136. public static final String DISABLED_CLASSNAME = "v-disabled";
  137. public static final String REQUIRED_CLASSNAME = "v-required";
  138. public static final String REQUIRED_CLASSNAME_EXT = "-required";
  139. public static final String ERROR_CLASSNAME_EXT = "-error";
  140. /**
  141. * A string that, if found in a non-JSON response to a UIDL request, will
  142. * cause the browser to refresh the page. If followed by a colon, optional
  143. * whitespace, and a URI, causes the browser to synchronously load the URI.
  144. *
  145. * <p>
  146. * This allows, for instance, a servlet filter to redirect the application
  147. * to a custom login page when the session expires. For example:
  148. * </p>
  149. *
  150. * <pre>
  151. * if (sessionExpired) {
  152. * response.setHeader(&quot;Content-Type&quot;, &quot;text/html&quot;);
  153. * response.getWriter().write(
  154. * myLoginPageHtml + &quot;&lt;!-- Vaadin-Refresh: &quot;
  155. * + request.getContextPath() + &quot; --&gt;&quot;);
  156. * }
  157. * </pre>
  158. */
  159. public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh";
  160. // will hold the CSRF token once received
  161. private String csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
  162. private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
  163. /**
  164. * The pending method invocations that will be send to the server by
  165. * {@link #sendPendingCommand}. The key is defined differently based on
  166. * whether the method invocation is enqueued with lastonly. With lastonly
  167. * enabled, the method signature ( {@link MethodInvocation#getLastOnlyTag()}
  168. * ) is used as the key to make enable removing a previously enqueued
  169. * invocation. Without lastonly, an incremental id based on
  170. * {@link #lastInvocationTag} is used to get unique values.
  171. */
  172. private LinkedHashMap<String, MethodInvocation> pendingInvocations = new LinkedHashMap<String, MethodInvocation>();
  173. private int lastInvocationTag = 0;
  174. private WidgetSet widgetSet;
  175. private VContextMenu contextMenu = null;
  176. private final UIConnector uIConnector;
  177. protected boolean applicationRunning = false;
  178. private boolean hasActiveRequest = false;
  179. /**
  180. * Some browsers cancel pending XHR requests when a request that might
  181. * navigate away from the page starts (indicated by a beforeunload event).
  182. * In that case, we should just send the request again without displaying
  183. * any error.
  184. */
  185. private boolean retryCanceledActiveRequest = false;
  186. /**
  187. * Webkit will ignore outgoing requests while waiting for a response to a
  188. * navigation event (indicated by a beforeunload event). When this happens,
  189. * we should keep trying to send the request every now and then until there
  190. * is a response or until it throws an exception saying that it is already
  191. * being sent.
  192. */
  193. private boolean webkitMaybeIgnoringRequests = false;
  194. protected boolean cssLoaded = false;
  195. /** Parameters for this application connection loaded from the web-page */
  196. private ApplicationConfiguration configuration;
  197. /** List of pending variable change bursts that must be submitted in order */
  198. private final ArrayList<LinkedHashMap<String, MethodInvocation>> pendingBursts = new ArrayList<LinkedHashMap<String, MethodInvocation>>();
  199. /** Timer for automatic refirect to SessionExpiredURL */
  200. private Timer redirectTimer;
  201. /** redirectTimer scheduling interval in seconds */
  202. private int sessionExpirationInterval;
  203. private Date requestStartTime;
  204. private final LayoutManager layoutManager;
  205. private final RpcManager rpcManager;
  206. private PushConnection push;
  207. /**
  208. * If responseHandlingLocks contains any objects, response handling is
  209. * suspended until the collection is empty or a timeout has occurred.
  210. */
  211. private Set<Object> responseHandlingLocks = new HashSet<Object>();
  212. /**
  213. * Data structure holding information about pending UIDL messages.
  214. */
  215. private class PendingUIDLMessage {
  216. private Date start;
  217. private String jsonText;
  218. private ValueMap json;
  219. public PendingUIDLMessage(Date start, String jsonText, ValueMap json) {
  220. this.start = start;
  221. this.jsonText = jsonText;
  222. this.json = json;
  223. }
  224. public Date getStart() {
  225. return start;
  226. }
  227. public String getJsonText() {
  228. return jsonText;
  229. }
  230. public ValueMap getJson() {
  231. return json;
  232. }
  233. }
  234. /** Contains all UIDL messages received while response handling is suspended */
  235. private List<PendingUIDLMessage> pendingUIDLMessages = new ArrayList<PendingUIDLMessage>();
  236. /** The max timeout that response handling may be suspended */
  237. private static final int MAX_SUSPENDED_TIMEOUT = 5000;
  238. /** Event bus for communication events */
  239. private EventBus eventBus = GWT.create(SimpleEventBus.class);
  240. /**
  241. * The communication handler methods are called at certain points during
  242. * communication with the server. This allows for making add-ons that keep
  243. * track of different aspects of the communication.
  244. */
  245. public interface CommunicationHandler extends EventHandler {
  246. void onRequestStarting(RequestStartingEvent e);
  247. void onResponseHandlingStarted(ResponseHandlingStartedEvent e);
  248. void onResponseHandlingEnded(ResponseHandlingEndedEvent e);
  249. }
  250. public static class RequestStartingEvent extends ApplicationConnectionEvent {
  251. public static Type<CommunicationHandler> TYPE = new Type<CommunicationHandler>();
  252. public RequestStartingEvent(ApplicationConnection connection) {
  253. super(connection);
  254. }
  255. @Override
  256. public Type<CommunicationHandler> getAssociatedType() {
  257. return TYPE;
  258. }
  259. @Override
  260. protected void dispatch(CommunicationHandler handler) {
  261. handler.onRequestStarting(this);
  262. }
  263. }
  264. public static class ResponseHandlingEndedEvent extends
  265. ApplicationConnectionEvent {
  266. public static Type<CommunicationHandler> TYPE = new Type<CommunicationHandler>();
  267. public ResponseHandlingEndedEvent(ApplicationConnection connection) {
  268. super(connection);
  269. }
  270. @Override
  271. public Type<CommunicationHandler> getAssociatedType() {
  272. return TYPE;
  273. }
  274. @Override
  275. protected void dispatch(CommunicationHandler handler) {
  276. handler.onResponseHandlingEnded(this);
  277. }
  278. }
  279. public static abstract class ApplicationConnectionEvent extends
  280. GwtEvent<CommunicationHandler> {
  281. private ApplicationConnection connection;
  282. protected ApplicationConnectionEvent(ApplicationConnection connection) {
  283. this.connection = connection;
  284. }
  285. public ApplicationConnection getConnection() {
  286. return connection;
  287. }
  288. }
  289. /**
  290. * Event triggered when a XHR request has finished with the status code of
  291. * the response.
  292. *
  293. * Useful for handlers observing network failures like online/off-line
  294. * monitors.
  295. */
  296. public static class ConnectionStatusEvent extends
  297. GwtEvent<ConnectionStatusEvent.ConnectionStatusHandler> {
  298. private int status;
  299. public static interface ConnectionStatusHandler extends EventHandler {
  300. public void onConnectionStatusChange(ConnectionStatusEvent event);
  301. }
  302. public ConnectionStatusEvent(int status) {
  303. this.status = status;
  304. }
  305. public int getStatus() {
  306. return status;
  307. }
  308. public final static Type<ConnectionStatusHandler> TYPE = new Type<ConnectionStatusHandler>();
  309. @Override
  310. public Type<ConnectionStatusHandler> getAssociatedType() {
  311. return TYPE;
  312. }
  313. @Override
  314. protected void dispatch(ConnectionStatusHandler handler) {
  315. handler.onConnectionStatusChange(this);
  316. }
  317. }
  318. public static class ResponseHandlingStartedEvent extends
  319. ApplicationConnectionEvent {
  320. public ResponseHandlingStartedEvent(ApplicationConnection connection) {
  321. super(connection);
  322. }
  323. public static Type<CommunicationHandler> TYPE = new Type<CommunicationHandler>();
  324. @Override
  325. public Type<CommunicationHandler> getAssociatedType() {
  326. return TYPE;
  327. }
  328. @Override
  329. protected void dispatch(CommunicationHandler handler) {
  330. handler.onResponseHandlingStarted(this);
  331. }
  332. }
  333. /**
  334. * Event triggered when a application is stopped by calling
  335. * {@link ApplicationConnection#setApplicationRunning(false)}.
  336. *
  337. * To listen for the event add a {@link ApplicationStoppedHandler} by
  338. * invoking
  339. * {@link ApplicationConnection#addHandler(ApplicationConnection.ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
  340. * to the {@link ApplicationConnection}
  341. *
  342. * @since 7.1.8
  343. * @author Vaadin Ltd
  344. */
  345. public static class ApplicationStoppedEvent extends
  346. GwtEvent<ApplicationStoppedHandler> {
  347. public static Type<ApplicationStoppedHandler> TYPE = new Type<ApplicationStoppedHandler>();
  348. @Override
  349. public Type<ApplicationStoppedHandler> getAssociatedType() {
  350. return TYPE;
  351. }
  352. @Override
  353. protected void dispatch(ApplicationStoppedHandler listener) {
  354. listener.onApplicationStopped(this);
  355. }
  356. }
  357. /**
  358. * Allows custom handling of communication errors.
  359. */
  360. public interface CommunicationErrorHandler {
  361. /**
  362. * Called when a communication error has occurred. Returning
  363. * <code>true</code> from this method suppresses error handling.
  364. *
  365. * @param details
  366. * A string describing the error.
  367. * @param statusCode
  368. * The HTTP status code (e.g. 404, etc).
  369. * @return true if the error reporting should be suppressed, false to
  370. * perform normal error reporting.
  371. */
  372. public boolean onError(String details, int statusCode);
  373. }
  374. /**
  375. * A listener for listening to application stopped events. The listener can
  376. * be added to a {@link ApplicationConnection} by invoking
  377. * {@link ApplicationConnection#addHandler(ApplicationStoppedEvent.Type, ApplicationStoppedHandler)}
  378. *
  379. * @since 7.1.8
  380. * @author Vaadin Ltd
  381. */
  382. public interface ApplicationStoppedHandler extends EventHandler {
  383. /**
  384. * Triggered when the {@link ApplicationConnection} marks a previously
  385. * running application as stopped by invoking
  386. * {@link ApplicationConnection#setApplicationRunning(false)}
  387. *
  388. * @param event
  389. * the event triggered by the {@link ApplicationConnection}
  390. */
  391. void onApplicationStopped(ApplicationStoppedEvent event);
  392. }
  393. private CommunicationErrorHandler communicationErrorDelegate = null;
  394. private VLoadingIndicator loadingIndicator;
  395. private Heartbeat heartbeat = GWT.create(Heartbeat.class);
  396. private boolean tooltipInitialized = false;
  397. public static class MultiStepDuration extends Duration {
  398. private int previousStep = elapsedMillis();
  399. public void logDuration(String message) {
  400. logDuration(message, 0);
  401. }
  402. public void logDuration(String message, int minDuration) {
  403. int currentTime = elapsedMillis();
  404. int stepDuration = currentTime - previousStep;
  405. if (stepDuration >= minDuration) {
  406. VConsole.log(message + ": " + stepDuration + " ms");
  407. }
  408. previousStep = currentTime;
  409. }
  410. }
  411. public ApplicationConnection() {
  412. // Assuming UI data is eagerly loaded
  413. ConnectorBundleLoader.get().loadBundle(
  414. ConnectorBundleLoader.EAGER_BUNDLE_NAME, null);
  415. uIConnector = GWT.create(UIConnector.class);
  416. rpcManager = GWT.create(RpcManager.class);
  417. layoutManager = GWT.create(LayoutManager.class);
  418. layoutManager.setConnection(this);
  419. tooltip = GWT.create(VTooltip.class);
  420. loadingIndicator = GWT.create(VLoadingIndicator.class);
  421. loadingIndicator.setConnection(this);
  422. }
  423. public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) {
  424. VConsole.log("Starting application " + cnf.getRootPanelId());
  425. VConsole.log("Using theme: " + cnf.getThemeName());
  426. VConsole.log("Vaadin application servlet version: "
  427. + cnf.getServletVersion());
  428. if (!cnf.getServletVersion().equals(Version.getFullVersion())) {
  429. VConsole.error("Warning: your widget set seems to be built with a different "
  430. + "version than the one used on server. Unexpected "
  431. + "behavior may occur.");
  432. }
  433. this.widgetSet = widgetSet;
  434. configuration = cnf;
  435. ComponentLocator componentLocator = new ComponentLocator(this);
  436. String appRootPanelName = cnf.getRootPanelId();
  437. // remove the end (window name) of autogenerated rootpanel id
  438. appRootPanelName = appRootPanelName.replaceFirst("-\\d+$", "");
  439. initializeTestbenchHooks(componentLocator, appRootPanelName);
  440. initializeClientHooks();
  441. uIConnector.init(cnf.getRootPanelId(), this);
  442. tooltip.setOwner(uIConnector.getWidget());
  443. getLoadingIndicator().show();
  444. heartbeat.init(this);
  445. Window.addWindowClosingHandler(new ClosingHandler() {
  446. @Override
  447. public void onWindowClosing(ClosingEvent event) {
  448. /*
  449. * Set some flags to avoid potential problems with XHR requests,
  450. * see javadocs of the flags for details
  451. */
  452. if (hasActiveRequest()) {
  453. retryCanceledActiveRequest = true;
  454. }
  455. webkitMaybeIgnoringRequests = true;
  456. }
  457. });
  458. // Ensure the overlay container is added to the dom and set as a live
  459. // area for assistive devices
  460. Element overlayContainer = VOverlay.getOverlayContainer(this);
  461. Roles.getAlertRole().setAriaLiveProperty(overlayContainer,
  462. LiveValue.ASSERTIVE);
  463. VOverlay.setOverlayContainerLabel(this,
  464. getUIConnector().getState().overlayContainerLabel);
  465. Roles.getAlertRole().setAriaRelevantProperty(overlayContainer,
  466. RelevantValue.ADDITIONS);
  467. }
  468. /**
  469. * Starts this application. Don't call this method directly - it's called by
  470. * {@link ApplicationConfiguration#startNextApplication()}, which should be
  471. * called once this application has started (first response received) or
  472. * failed to start. This ensures that the applications are started in order,
  473. * to avoid session-id problems.
  474. *
  475. */
  476. public void start() {
  477. String jsonText = configuration.getUIDL();
  478. if (jsonText == null) {
  479. // inital UIDL not in DOM, request later
  480. repaintAll();
  481. } else {
  482. // Update counter so TestBench knows something is still going on
  483. hasActiveRequest = true;
  484. // initial UIDL provided in DOM, continue as if returned by request
  485. handleJSONText(jsonText, -1);
  486. }
  487. // Tooltip can't be created earlier because the
  488. // necessary fields are not setup to add it in the
  489. // correct place in the DOM
  490. if (!tooltipInitialized) {
  491. tooltipInitialized = true;
  492. ApplicationConfiguration.runWhenDependenciesLoaded(new Command() {
  493. @Override
  494. public void execute() {
  495. getVTooltip().initializeAssistiveTooltips();
  496. }
  497. });
  498. }
  499. }
  500. /**
  501. * Checks if there is some work to be done on the client side
  502. *
  503. * @return true if the client has some work to be done, false otherwise
  504. */
  505. private boolean isActive() {
  506. return isWorkPending() || hasActiveRequest()
  507. || isExecutingDeferredCommands();
  508. }
  509. private native void initializeTestbenchHooks(
  510. ComponentLocator componentLocator, String TTAppId)
  511. /*-{
  512. var ap = this;
  513. var client = {};
  514. client.isActive = $entry(function() {
  515. return ap.@com.vaadin.client.ApplicationConnection::isActive()();
  516. });
  517. var vi = ap.@com.vaadin.client.ApplicationConnection::getVersionInfo()();
  518. if (vi) {
  519. client.getVersionInfo = function() {
  520. return vi;
  521. }
  522. }
  523. client.getProfilingData = $entry(function() {
  524. var pd = [
  525. ap.@com.vaadin.client.ApplicationConnection::lastProcessingTime,
  526. ap.@com.vaadin.client.ApplicationConnection::totalProcessingTime
  527. ];
  528. pd = pd.concat(ap.@com.vaadin.client.ApplicationConnection::serverTimingInfo);
  529. pd[pd.length] = ap.@com.vaadin.client.ApplicationConnection::bootstrapTime;
  530. return pd;
  531. });
  532. client.getElementByPath = $entry(function(id) {
  533. return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
  534. });
  535. client.getElementByPathStartingAt = $entry(function(id, element) {
  536. return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
  537. });
  538. client.getElementsByPath = $entry(function(id) {
  539. return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPath(Ljava/lang/String;)(id);
  540. });
  541. client.getElementsByPathStartingAt = $entry(function(id, element) {
  542. return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/dom/client/Element;)(id, element);
  543. });
  544. client.getPathForElement = $entry(function(element) {
  545. return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getLegacyPathForElement(Lcom/google/gwt/dom/client/Element;)(element);
  546. });
  547. client.initializing = false;
  548. $wnd.vaadin.clients[TTAppId] = client;
  549. }-*/;
  550. private static native final int calculateBootstrapTime()
  551. /*-{
  552. if ($wnd.performance && $wnd.performance.timing) {
  553. return (new Date).getTime() - $wnd.performance.timing.responseStart;
  554. } else {
  555. // performance.timing not supported
  556. return -1;
  557. }
  558. }-*/;
  559. /**
  560. * Helper for tt initialization
  561. */
  562. private JavaScriptObject getVersionInfo() {
  563. return configuration.getVersionInfoJSObject();
  564. }
  565. /**
  566. * Publishes a JavaScript API for mash-up applications.
  567. * <ul>
  568. * <li><code>vaadin.forceSync()</code> sends pending variable changes, in
  569. * effect synchronizing the server and client state. This is done for all
  570. * applications on host page.</li>
  571. * <li><code>vaadin.postRequestHooks</code> is a map of functions which gets
  572. * called after each XHR made by vaadin application. Note, that it is
  573. * attaching js functions responsibility to create the variable like this:
  574. *
  575. * <code><pre>
  576. * if(!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();}
  577. * postRequestHooks.myHook = function(appId) {
  578. * if(appId == "MyAppOfInterest") {
  579. * // do the staff you need on xhr activity
  580. * }
  581. * }
  582. * </pre></code> First parameter passed to these functions is the identifier
  583. * of Vaadin application that made the request.
  584. * </ul>
  585. *
  586. * TODO make this multi-app aware
  587. */
  588. private native void initializeClientHooks()
  589. /*-{
  590. var app = this;
  591. var oldSync;
  592. if ($wnd.vaadin.forceSync) {
  593. oldSync = $wnd.vaadin.forceSync;
  594. }
  595. $wnd.vaadin.forceSync = $entry(function() {
  596. if (oldSync) {
  597. oldSync();
  598. }
  599. app.@com.vaadin.client.ApplicationConnection::sendPendingVariableChanges()();
  600. });
  601. var oldForceLayout;
  602. if ($wnd.vaadin.forceLayout) {
  603. oldForceLayout = $wnd.vaadin.forceLayout;
  604. }
  605. $wnd.vaadin.forceLayout = $entry(function() {
  606. if (oldForceLayout) {
  607. oldForceLayout();
  608. }
  609. app.@com.vaadin.client.ApplicationConnection::forceLayout()();
  610. });
  611. }-*/;
  612. /**
  613. * Runs possibly registered client side post request hooks. This is expected
  614. * to be run after each uidl request made by Vaadin application.
  615. *
  616. * @param appId
  617. */
  618. private static native void runPostRequestHooks(String appId)
  619. /*-{
  620. if ($wnd.vaadin.postRequestHooks) {
  621. for ( var hook in $wnd.vaadin.postRequestHooks) {
  622. if (typeof ($wnd.vaadin.postRequestHooks[hook]) == "function") {
  623. try {
  624. $wnd.vaadin.postRequestHooks[hook](appId);
  625. } catch (e) {
  626. }
  627. }
  628. }
  629. }
  630. }-*/;
  631. /**
  632. * If on Liferay and logged in, ask the client side session management
  633. * JavaScript to extend the session duration.
  634. *
  635. * Otherwise, Liferay client side JavaScript will explicitly expire the
  636. * session even though the server side considers the session to be active.
  637. * See ticket #8305 for more information.
  638. */
  639. protected native void extendLiferaySession()
  640. /*-{
  641. if ($wnd.Liferay && $wnd.Liferay.Session) {
  642. $wnd.Liferay.Session.extend();
  643. // if the extend banner is visible, hide it
  644. if ($wnd.Liferay.Session.banner) {
  645. $wnd.Liferay.Session.banner.remove();
  646. }
  647. }
  648. }-*/;
  649. /**
  650. * Indicates whether or not there are currently active UIDL requests. Used
  651. * internally to sequence requests properly, seldom needed in Widgets.
  652. *
  653. * @return true if there are active requests
  654. */
  655. public boolean hasActiveRequest() {
  656. return hasActiveRequest;
  657. }
  658. private String getRepaintAllParameters() {
  659. String parameters = ApplicationConstants.URL_PARAMETER_REPAINT_ALL
  660. + "=1";
  661. return parameters;
  662. }
  663. protected void repaintAll() {
  664. makeUidlRequest(new JSONArray(), getRepaintAllParameters());
  665. }
  666. /**
  667. * Requests an analyze of layouts, to find inconsistencies. Exclusively used
  668. * for debugging during development.
  669. *
  670. * @deprecated as of 7.1. Replaced by {@link UIConnector#analyzeLayouts()}
  671. */
  672. @Deprecated
  673. public void analyzeLayouts() {
  674. getUIConnector().analyzeLayouts();
  675. }
  676. /**
  677. * Sends a request to the server to print details to console that will help
  678. * the developer to locate the corresponding server-side connector in the
  679. * source code.
  680. *
  681. * @param serverConnector
  682. * @deprecated as of 7.1. Replaced by
  683. * {@link UIConnector#showServerDebugInfo(ServerConnector)}
  684. */
  685. @Deprecated
  686. void highlightConnector(ServerConnector serverConnector) {
  687. getUIConnector().showServerDebugInfo(serverConnector);
  688. }
  689. /**
  690. * Makes an UIDL request to the server.
  691. *
  692. * @param reqInvocations
  693. * Data containing RPC invocations and all related information.
  694. * @param extraParams
  695. * Parameters that are added as GET parameters to the url.
  696. * Contains key=value pairs joined by & characters or is empty if
  697. * no parameters should be added. Should not start with any
  698. * special character.
  699. */
  700. protected void makeUidlRequest(final JSONArray reqInvocations,
  701. final String extraParams) {
  702. startRequest();
  703. JSONObject payload = new JSONObject();
  704. if (!getCsrfToken().equals(
  705. ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
  706. payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString(
  707. getCsrfToken()));
  708. }
  709. payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations);
  710. payload.put(ApplicationConstants.SERVER_SYNC_ID, new JSONNumber(
  711. lastSeenServerSyncId));
  712. VConsole.log("Making UIDL Request with params: " + payload);
  713. String uri = translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
  714. + ApplicationConstants.UIDL_PATH + '/');
  715. if (extraParams != null && extraParams.length() > 0) {
  716. uri = addGetParameters(uri, extraParams);
  717. }
  718. uri = addGetParameters(uri, UIConstants.UI_ID_PARAMETER + "="
  719. + configuration.getUIId());
  720. doUidlRequest(uri, payload);
  721. }
  722. /**
  723. * Sends an asynchronous or synchronous UIDL request to the server using the
  724. * given URI.
  725. *
  726. * @param uri
  727. * The URI to use for the request. May includes GET parameters
  728. * @param payload
  729. * The contents of the request to send
  730. */
  731. protected void doUidlRequest(final String uri, final JSONObject payload) {
  732. RequestCallback requestCallback = new RequestCallback() {
  733. @Override
  734. public void onError(Request request, Throwable exception) {
  735. handleCommunicationError(exception.getMessage(), -1);
  736. }
  737. private void handleCommunicationError(String details, int statusCode) {
  738. if (!handleErrorInDelegate(details, statusCode)) {
  739. showCommunicationError(details, statusCode);
  740. }
  741. endRequest();
  742. // Consider application not running any more and prevent all
  743. // future requests
  744. setApplicationRunning(false);
  745. }
  746. @Override
  747. public void onResponseReceived(Request request, Response response) {
  748. VConsole.log("Server visit took "
  749. + String.valueOf((new Date()).getTime()
  750. - requestStartTime.getTime()) + "ms");
  751. int statusCode = response.getStatusCode();
  752. // Notify network observers about response status
  753. fireEvent(new ConnectionStatusEvent(statusCode));
  754. switch (statusCode) {
  755. case 0:
  756. if (retryCanceledActiveRequest) {
  757. /*
  758. * Request was most likely canceled because the browser
  759. * is maybe navigating away from the page. Just send the
  760. * request again without displaying any error in case
  761. * the navigation isn't carried through.
  762. */
  763. retryCanceledActiveRequest = false;
  764. doUidlRequest(uri, payload);
  765. } else {
  766. handleCommunicationError(
  767. "Invalid status code 0 (server down?)",
  768. statusCode);
  769. }
  770. return;
  771. case 401:
  772. /*
  773. * Authorization has failed. Could be that the session has
  774. * timed out and the container is redirecting to a login
  775. * page.
  776. */
  777. showAuthenticationError("");
  778. endRequest();
  779. return;
  780. case 503:
  781. /*
  782. * We'll assume msec instead of the usual seconds. If
  783. * there's no Retry-After header, handle the error like a
  784. * 500, as per RFC 2616 section 10.5.4.
  785. */
  786. String delay = response.getHeader("Retry-After");
  787. if (delay != null) {
  788. VConsole.log("503, retrying in " + delay + "msec");
  789. (new Timer() {
  790. @Override
  791. public void run() {
  792. doUidlRequest(uri, payload);
  793. }
  794. }).schedule(Integer.parseInt(delay));
  795. return;
  796. }
  797. }
  798. if ((statusCode / 100) == 4) {
  799. // Handle all 4xx errors the same way as (they are
  800. // all permanent errors)
  801. showCommunicationError(
  802. "UIDL could not be read from server. Check servlets mappings. Error code: "
  803. + statusCode, statusCode);
  804. endRequest();
  805. return;
  806. } else if ((statusCode / 100) == 5) {
  807. // Something's wrong on the server, there's nothing the
  808. // client can do except maybe try again.
  809. handleCommunicationError("Server error. Error code: "
  810. + statusCode, statusCode);
  811. return;
  812. }
  813. String contentType = response.getHeader("Content-Type");
  814. if (contentType == null
  815. || !contentType.startsWith("application/json")) {
  816. /*
  817. * A servlet filter or equivalent may have intercepted the
  818. * request and served non-UIDL content (for instance, a
  819. * login page if the session has expired.) If the response
  820. * contains a magic substring, do a synchronous refresh. See
  821. * #8241.
  822. */
  823. MatchResult refreshToken = RegExp.compile(
  824. UIDL_REFRESH_TOKEN + "(:\\s*(.*?))?(\\s|$)").exec(
  825. response.getText());
  826. if (refreshToken != null) {
  827. redirect(refreshToken.getGroup(2));
  828. return;
  829. }
  830. }
  831. // for(;;);[realjson]
  832. final String jsonText = response.getText().substring(9,
  833. response.getText().length() - 1);
  834. handleJSONText(jsonText, statusCode);
  835. }
  836. };
  837. if (push != null) {
  838. push.push(payload);
  839. } else {
  840. try {
  841. doAjaxRequest(uri, payload, requestCallback);
  842. } catch (RequestException e) {
  843. VConsole.error(e);
  844. endRequest();
  845. fireEvent(new ConnectionStatusEvent(0));
  846. }
  847. }
  848. }
  849. /**
  850. * Handles received UIDL JSON text, parsing it, and passing it on to the
  851. * appropriate handlers, while logging timing information.
  852. *
  853. * @param jsonText
  854. * @param statusCode
  855. */
  856. private void handleJSONText(String jsonText, int statusCode) {
  857. final Date start = new Date();
  858. final ValueMap json;
  859. try {
  860. json = parseJSONResponse(jsonText);
  861. } catch (final Exception e) {
  862. endRequest();
  863. showCommunicationError(e.getMessage() + " - Original JSON-text:"
  864. + jsonText, statusCode);
  865. return;
  866. }
  867. VConsole.log("JSON parsing took "
  868. + (new Date().getTime() - start.getTime()) + "ms");
  869. if (isApplicationRunning()) {
  870. handleReceivedJSONMessage(start, jsonText, json);
  871. } else {
  872. setApplicationRunning(true);
  873. handleWhenCSSLoaded(jsonText, json);
  874. }
  875. }
  876. /**
  877. * Sends an asynchronous UIDL request to the server using the given URI.
  878. *
  879. * @param uri
  880. * The URI to use for the request. May includes GET parameters
  881. * @param payload
  882. * The contents of the request to send
  883. * @param requestCallback
  884. * The handler for the response
  885. * @throws RequestException
  886. * if the request could not be sent
  887. */
  888. protected void doAjaxRequest(String uri, JSONObject payload,
  889. RequestCallback requestCallback) throws RequestException {
  890. RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
  891. // TODO enable timeout
  892. // rb.setTimeoutMillis(timeoutMillis);
  893. // TODO this should be configurable
  894. rb.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);
  895. rb.setRequestData(payload.toString());
  896. rb.setCallback(requestCallback);
  897. final Request request = rb.send();
  898. if (webkitMaybeIgnoringRequests && BrowserInfo.get().isWebkit()) {
  899. final int retryTimeout = 250;
  900. new Timer() {
  901. @Override
  902. public void run() {
  903. // Use native js to access private field in Request
  904. if (resendRequest(request) && webkitMaybeIgnoringRequests) {
  905. // Schedule retry if still needed
  906. schedule(retryTimeout);
  907. }
  908. }
  909. }.schedule(retryTimeout);
  910. }
  911. }
  912. private static native boolean resendRequest(Request request)
  913. /*-{
  914. var xhr = request.@com.google.gwt.http.client.Request::xmlHttpRequest
  915. if (xhr.readyState != 1) {
  916. // Progressed to some other readyState -> no longer blocked
  917. return false;
  918. }
  919. try {
  920. xhr.send();
  921. return true;
  922. } catch (e) {
  923. // send throws exception if it is running for real
  924. return false;
  925. }
  926. }-*/;
  927. int cssWaits = 0;
  928. /**
  929. * Holds the time spent rendering the last request
  930. */
  931. protected int lastProcessingTime;
  932. /**
  933. * Holds the total time spent rendering requests during the lifetime of the
  934. * session.
  935. */
  936. protected int totalProcessingTime;
  937. /**
  938. * Holds the time it took to load the page and render the first view. 0
  939. * means that this value has not yet been calculated because the first view
  940. * has not yet been rendered (or that your browser is very fast). -1 means
  941. * that the browser does not support the performance.timing feature used to
  942. * get this measurement.
  943. */
  944. private int bootstrapTime;
  945. /**
  946. * Holds the timing information from the server-side. How much time was
  947. * spent servicing the last request and how much time has been spent
  948. * servicing the session so far. These values are always one request behind,
  949. * since they cannot be measured before the request is finished.
  950. */
  951. private ValueMap serverTimingInfo;
  952. /**
  953. * Holds the last seen response id given by the server.
  954. * <p>
  955. * The server generates a strictly increasing id for each response to each
  956. * request from the client. This ID is then replayed back to the server on
  957. * each request. This helps the server in knowing in what state the client
  958. * is, and compare it to its own state. In short, it helps with concurrent
  959. * changes between the client and server.
  960. * <p>
  961. * Initial value, i.e. no responses received from the server, is
  962. * {@link #UNDEFINED_SYNC_ID} ({@value #UNDEFINED_SYNC_ID}). This happens
  963. * between the bootstrap HTML being loaded and the first UI being rendered;
  964. */
  965. private int lastSeenServerSyncId = UNDEFINED_SYNC_ID;
  966. /**
  967. * The value of an undefined sync id.
  968. * <p>
  969. * This must be <code>-1</code>, because of the contract in
  970. * {@link #getLastResponseId()}
  971. */
  972. private static final int UNDEFINED_SYNC_ID = -1;
  973. static final int MAX_CSS_WAITS = 100;
  974. protected void handleWhenCSSLoaded(final String jsonText,
  975. final ValueMap json) {
  976. if (!isCSSLoaded() && cssWaits < MAX_CSS_WAITS) {
  977. (new Timer() {
  978. @Override
  979. public void run() {
  980. handleWhenCSSLoaded(jsonText, json);
  981. }
  982. }).schedule(50);
  983. // Show this message just once
  984. if (cssWaits++ == 0) {
  985. VConsole.log("Assuming CSS loading is not complete, "
  986. + "postponing render phase. "
  987. + "(.v-loading-indicator height == 0)");
  988. }
  989. } else {
  990. cssLoaded = true;
  991. if (cssWaits >= MAX_CSS_WAITS) {
  992. getLogger().severe("CSS files may have not loaded properly.");
  993. }
  994. handleReceivedJSONMessage(new Date(), jsonText, json);
  995. }
  996. }
  997. /**
  998. * Checks whether or not the CSS is loaded. By default checks the size of
  999. * the loading indicator element.
  1000. *
  1001. * @return
  1002. */
  1003. protected boolean isCSSLoaded() {
  1004. return cssLoaded
  1005. || getLoadingIndicator().getElement().getOffsetHeight() != 0;
  1006. }
  1007. /**
  1008. * Shows the communication error notification.
  1009. *
  1010. * @param details
  1011. * Optional details for debugging.
  1012. * @param statusCode
  1013. * The status code returned for the request
  1014. *
  1015. */
  1016. protected void showCommunicationError(String details, int statusCode) {
  1017. VConsole.error("Communication error: " + details);
  1018. showError(details, configuration.getCommunicationError());
  1019. }
  1020. /**
  1021. * Shows the authentication error notification.
  1022. *
  1023. * @param details
  1024. * Optional details for debugging.
  1025. */
  1026. protected void showAuthenticationError(String details) {
  1027. VConsole.error("Authentication error: " + details);
  1028. showError(details, configuration.getAuthorizationError());
  1029. }
  1030. /**
  1031. * Shows the session expiration notification.
  1032. *
  1033. * @param details
  1034. * Optional details for debugging.
  1035. */
  1036. public void showSessionExpiredError(String details) {
  1037. VConsole.error("Session expired: " + details);
  1038. showError(details, configuration.getSessionExpiredError());
  1039. }
  1040. /**
  1041. * Shows an error notification.
  1042. *
  1043. * @param details
  1044. * Optional details for debugging.
  1045. * @param message
  1046. * An ErrorMessage describing the error.
  1047. */
  1048. protected void showError(String details, ErrorMessage message) {
  1049. showError(details, message.getCaption(), message.getMessage(),
  1050. message.getUrl());
  1051. }
  1052. /**
  1053. * Shows the error notification.
  1054. *
  1055. * @param details
  1056. * Optional details for debugging.
  1057. */
  1058. private void showError(String details, String caption, String message,
  1059. String url) {
  1060. StringBuilder html = new StringBuilder();
  1061. if (caption != null) {
  1062. html.append("<h1>");
  1063. html.append(caption);
  1064. html.append("</h1>");
  1065. }
  1066. if (message != null) {
  1067. html.append("<p>");
  1068. html.append(message);
  1069. html.append("</p>");
  1070. }
  1071. if (html.length() > 0) {
  1072. // Add error description
  1073. if (details != null) {
  1074. html.append("<p><i style=\"font-size:0.7em\">");
  1075. html.append(details);
  1076. html.append("</i></p>");
  1077. }
  1078. VNotification n = VNotification.createNotification(1000 * 60 * 45,
  1079. uIConnector.getWidget());
  1080. n.addEventListener(new NotificationRedirect(url));
  1081. n.show(html.toString(), VNotification.CENTERED_TOP,
  1082. VNotification.STYLE_SYSTEM);
  1083. } else {
  1084. redirect(url);
  1085. }
  1086. }
  1087. protected void startRequest() {
  1088. if (hasActiveRequest) {
  1089. VConsole.error("Trying to start a new request while another is active");
  1090. }
  1091. hasActiveRequest = true;
  1092. requestStartTime = new Date();
  1093. loadingIndicator.trigger();
  1094. eventBus.fireEvent(new RequestStartingEvent(this));
  1095. }
  1096. protected void endRequest() {
  1097. if (!hasActiveRequest) {
  1098. VConsole.error("No active request");
  1099. }
  1100. // After checkForPendingVariableBursts() there may be a new active
  1101. // request, so we must set hasActiveRequest to false before, not after,
  1102. // the call. Active requests used to be tracked with an integer counter,
  1103. // so setting it after used to work but not with the #8505 changes.
  1104. hasActiveRequest = false;
  1105. retryCanceledActiveRequest = false;
  1106. webkitMaybeIgnoringRequests = false;
  1107. if (isApplicationRunning()) {
  1108. checkForPendingVariableBursts();
  1109. runPostRequestHooks(configuration.getRootPanelId());
  1110. }
  1111. // deferring to avoid flickering
  1112. Scheduler.get().scheduleDeferred(new Command() {
  1113. @Override
  1114. public void execute() {
  1115. if (!hasActiveRequest()) {
  1116. getLoadingIndicator().hide();
  1117. // If on Liferay and session expiration management is in
  1118. // use, extend session duration on each request.
  1119. // Doing it here rather than before the request to improve
  1120. // responsiveness.
  1121. // Postponed until the end of the next request if other
  1122. // requests still pending.
  1123. extendLiferaySession();
  1124. }
  1125. }
  1126. });
  1127. eventBus.fireEvent(new ResponseHandlingEndedEvent(this));
  1128. }
  1129. /**
  1130. * This method is called after applying uidl change set to application.
  1131. *
  1132. * It will clean current and queued variable change sets. And send next
  1133. * change set if it exists.
  1134. */
  1135. private void checkForPendingVariableBursts() {
  1136. cleanVariableBurst(pendingInvocations);
  1137. if (pendingBursts.size() > 0) {
  1138. for (LinkedHashMap<String, MethodInvocation> pendingBurst : pendingBursts) {
  1139. cleanVariableBurst(pendingBurst);
  1140. }
  1141. LinkedHashMap<String, MethodInvocation> nextBurst = pendingBursts
  1142. .remove(0);
  1143. buildAndSendVariableBurst(nextBurst);
  1144. }
  1145. }
  1146. /**
  1147. * Cleans given queue of variable changes of such changes that came from
  1148. * components that do not exist anymore.
  1149. *
  1150. * @param variableBurst
  1151. */
  1152. private void cleanVariableBurst(
  1153. LinkedHashMap<String, MethodInvocation> variableBurst) {
  1154. Iterator<MethodInvocation> iterator = variableBurst.values().iterator();
  1155. while (iterator.hasNext()) {
  1156. String id = iterator.next().getConnectorId();
  1157. if (!getConnectorMap().hasConnector(id)
  1158. && !getConnectorMap().isDragAndDropPaintable(id)) {
  1159. // variable owner does not exist anymore
  1160. iterator.remove();
  1161. VConsole.log("Removed variable from removed component: " + id);
  1162. }
  1163. }
  1164. }
  1165. /**
  1166. * Checks if the client has running or scheduled commands
  1167. */
  1168. private boolean isWorkPending() {
  1169. ConnectorMap connectorMap = getConnectorMap();
  1170. JsArrayObject<ServerConnector> connectors = connectorMap
  1171. .getConnectorsAsJsArray();
  1172. int size = connectors.size();
  1173. for (int i = 0; i < size; i++) {
  1174. ServerConnector conn = connectors.get(i);
  1175. ComponentConnector compConn = null;
  1176. if (conn instanceof ComponentConnector) {
  1177. compConn = (ComponentConnector) conn;
  1178. Widget wgt = compConn.getWidget();
  1179. if (wgt instanceof DeferredWorker) {
  1180. if (((DeferredWorker) wgt).isWorkPending()) {
  1181. return true;
  1182. }
  1183. }
  1184. }
  1185. }
  1186. return false;
  1187. }
  1188. /**
  1189. * Checks if deferred commands are (potentially) still being executed as a
  1190. * result of an update from the server. Returns true if a deferred command
  1191. * might still be executing, false otherwise. This will not work correctly
  1192. * if a deferred command is added in another deferred command.
  1193. * <p>
  1194. * Used by the native "client.isActive" function.
  1195. * </p>
  1196. *
  1197. * @return true if deferred commands are (potentially) being executed, false
  1198. * otherwise
  1199. */
  1200. private boolean isExecutingDeferredCommands() {
  1201. Scheduler s = Scheduler.get();
  1202. if (s instanceof VSchedulerImpl) {
  1203. return ((VSchedulerImpl) s).hasWorkQueued();
  1204. } else {
  1205. return false;
  1206. }
  1207. }
  1208. /**
  1209. * Returns the loading indicator used by this ApplicationConnection
  1210. *
  1211. * @return The loading indicator for this ApplicationConnection
  1212. */
  1213. public VLoadingIndicator getLoadingIndicator() {
  1214. return loadingIndicator;
  1215. }
  1216. /**
  1217. * Determines whether or not the loading indicator is showing.
  1218. *
  1219. * @return true if the loading indicator is visible
  1220. * @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and
  1221. * {@link VLoadingIndicator#isVisible()}.isVisible() instead.
  1222. */
  1223. @Deprecated
  1224. public boolean isLoadingIndicatorVisible() {
  1225. return getLoadingIndicator().isVisible();
  1226. }
  1227. private static native ValueMap parseJSONResponse(String jsonText)
  1228. /*-{
  1229. try {
  1230. return JSON.parse(jsonText);
  1231. } catch (ignored) {
  1232. return eval('(' + jsonText + ')');
  1233. }
  1234. }-*/;
  1235. private void handleReceivedJSONMessage(Date start, String jsonText,
  1236. ValueMap json) {
  1237. handleUIDLMessage(start, jsonText, json);
  1238. }
  1239. /**
  1240. * Gets the id of the last received response. This id can be used by
  1241. * connectors to determine whether new data has been received from the
  1242. * server to avoid doing the same calculations multiple times.
  1243. * <p>
  1244. * No guarantees are made for the structure of the id other than that there
  1245. * will be a new unique value every time a new response with data from the
  1246. * server is received.
  1247. * <p>
  1248. * The initial id when no request has yet been processed is -1.
  1249. *
  1250. * @return and id identifying the response
  1251. */
  1252. public int getLastResponseId() {
  1253. /*
  1254. * The discrepancy between field name and getter name is simply historic
  1255. * - API can't be changed, but the field was repurposed in a more
  1256. * general, yet compatible, use. "Response id" was deemed unsuitable a
  1257. * name, so it was called "server sync id" instead.
  1258. */
  1259. return lastSeenServerSyncId;
  1260. }
  1261. protected void handleUIDLMessage(final Date start, final String jsonText,
  1262. final ValueMap json) {
  1263. if (!responseHandlingLocks.isEmpty()) {
  1264. // Some component is doing something that can't be interrupted
  1265. // (e.g. animation that should be smooth). Enqueue the UIDL
  1266. // message for later processing.
  1267. VConsole.log("Postponing UIDL handling due to lock...");
  1268. pendingUIDLMessages.add(new PendingUIDLMessage(start, jsonText,
  1269. json));
  1270. forceHandleMessage.schedule(MAX_SUSPENDED_TIMEOUT);
  1271. return;
  1272. }
  1273. /*
  1274. * Lock response handling to avoid a situation where something pushed
  1275. * from the server gets processed while waiting for e.g. lazily loaded
  1276. * connectors that are needed for processing the current message.
  1277. */
  1278. final Object lock = new Object();
  1279. suspendReponseHandling(lock);
  1280. VConsole.log("Handling message from server");
  1281. eventBus.fireEvent(new ResponseHandlingStartedEvent(this));
  1282. final int syncId;
  1283. if (json.containsKey(ApplicationConstants.SERVER_SYNC_ID)) {
  1284. syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID);
  1285. /*
  1286. * Use sync id unless explicitly set as undefined, as is done by
  1287. * e.g. critical server-side notifications
  1288. */
  1289. if (syncId != -1) {
  1290. assert (lastSeenServerSyncId == UNDEFINED_SYNC_ID || syncId == lastSeenServerSyncId + 1) : "Newly retrieved server sync id was not exactly one larger than the previous one (new: "
  1291. + syncId + ", last seen: " + lastSeenServerSyncId + ")";
  1292. lastSeenServerSyncId = syncId;
  1293. }
  1294. } else {
  1295. syncId = -1;
  1296. VConsole.error("Server response didn't contain a sync id. "
  1297. + "Please verify that the server is up-to-date and that the response data has not been modified in transmission.");
  1298. }
  1299. // Handle redirect
  1300. if (json.containsKey("redirect")) {
  1301. String url = json.getValueMap("redirect").getString("url");
  1302. VConsole.log("redirecting to " + url);
  1303. redirect(url);
  1304. return;
  1305. }
  1306. final MultiStepDuration handleUIDLDuration = new MultiStepDuration();
  1307. // Get security key
  1308. if (json.containsKey(ApplicationConstants.UIDL_SECURITY_TOKEN_ID)) {
  1309. csrfToken = json
  1310. .getString(ApplicationConstants.UIDL_SECURITY_TOKEN_ID);
  1311. }
  1312. VConsole.log(" * Handling resources from server");
  1313. if (json.containsKey("resources")) {
  1314. ValueMap resources = json.getValueMap("resources");
  1315. JsArrayString keyArray = resources.getKeyArray();
  1316. int l = keyArray.length();
  1317. for (int i = 0; i < l; i++) {
  1318. String key = keyArray.get(i);
  1319. resourcesMap.put(key, resources.getAsString(key));
  1320. }
  1321. }
  1322. handleUIDLDuration.logDuration(
  1323. " * Handling resources from server completed", 10);
  1324. VConsole.log(" * Handling type inheritance map from server");
  1325. if (json.containsKey("typeInheritanceMap")) {
  1326. configuration.addComponentInheritanceInfo(json
  1327. .getValueMap("typeInheritanceMap"));
  1328. }
  1329. handleUIDLDuration.logDuration(
  1330. " * Handling type inheritance map from server completed", 10);
  1331. VConsole.log("Handling type mappings from server");
  1332. if (json.containsKey("typeMappings")) {
  1333. configuration.addComponentMappings(
  1334. json.getValueMap("typeMappings"), widgetSet);
  1335. }
  1336. VConsole.log("Handling resource dependencies");
  1337. if (json.containsKey("scriptDependencies")) {
  1338. loadScriptDependencies(json.getJSStringArray("scriptDependencies"));
  1339. }
  1340. if (json.containsKey("styleDependencies")) {
  1341. loadStyleDependencies(json.getJSStringArray("styleDependencies"));
  1342. }
  1343. handleUIDLDuration.logDuration(
  1344. " * Handling type mappings from server completed", 10);
  1345. /*
  1346. * Hook for e.g. TestBench to get details about server peformance
  1347. */
  1348. if (json.containsKey("timings")) {
  1349. serverTimingInfo = json.getValueMap("timings");
  1350. }
  1351. Command c = new Command() {
  1352. private boolean onlyNoLayoutUpdates = true;
  1353. @Override
  1354. public void execute() {
  1355. assert syncId == -1 || syncId == lastSeenServerSyncId;
  1356. handleUIDLDuration.logDuration(" * Loading widgets completed",
  1357. 10);
  1358. Profiler.enter("Handling meta information");
  1359. ValueMap meta = null;
  1360. if (json.containsKey("meta")) {
  1361. VConsole.log(" * Handling meta information");
  1362. meta = json.getValueMap("meta");
  1363. if (meta.containsKey("repaintAll")) {
  1364. prepareRepaintAll();
  1365. }
  1366. if (meta.containsKey("timedRedirect")) {
  1367. final ValueMap timedRedirect = meta
  1368. .getValueMap("timedRedirect");
  1369. if (redirectTimer != null) {
  1370. redirectTimer.cancel();
  1371. }
  1372. redirectTimer = new Timer() {
  1373. @Override
  1374. public void run() {
  1375. redirect(timedRedirect.getString("url"));
  1376. }
  1377. };
  1378. sessionExpirationInterval = timedRedirect
  1379. .getInt("interval");
  1380. }
  1381. }
  1382. Profiler.leave("Handling meta information");
  1383. if (redirectTimer != null) {
  1384. redirectTimer.schedule(1000 * sessionExpirationInterval);
  1385. }
  1386. double processUidlStart = Duration.currentTimeMillis();
  1387. // Ensure that all connectors that we are about to update exist
  1388. JsArrayString createdConnectorIds = createConnectorsIfNeeded(json);
  1389. // Update states, do not fire events
  1390. JsArrayObject<StateChangeEvent> pendingStateChangeEvents = updateConnectorState(
  1391. json, createdConnectorIds);
  1392. /*
  1393. * Doing this here so that locales are available also to the
  1394. * connectors which get a state change event before the UI.
  1395. */
  1396. Profiler.enter("Handling locales");
  1397. VConsole.log(" * Handling locales");
  1398. // Store locale data
  1399. LocaleService
  1400. .addLocales(getUIConnector().getState().localeServiceState.localeData);
  1401. Profiler.leave("Handling locales");
  1402. // Update hierarchy, do not fire events
  1403. ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(json);
  1404. // Fire hierarchy change events
  1405. sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events);
  1406. updateCaptions(pendingStateChangeEvents,
  1407. connectorHierarchyUpdateResult.parentChangedIds);
  1408. delegateToWidget(pendingStateChangeEvents);
  1409. // Fire state change events.
  1410. sendStateChangeEvents(pendingStateChangeEvents);
  1411. // Update of legacy (UIDL) style connectors
  1412. updateVaadin6StyleConnectors(json);
  1413. // Handle any RPC invocations done on the server side
  1414. handleRpcInvocations(json);
  1415. if (json.containsKey("dd")) {
  1416. // response contains data for drag and drop service
  1417. VDragAndDropManager.get().handleServerResponse(
  1418. json.getValueMap("dd"));
  1419. }
  1420. unregisterRemovedConnectors();
  1421. VConsole.log("handleUIDLMessage: "
  1422. + (Duration.currentTimeMillis() - processUidlStart)
  1423. + " ms");
  1424. if (!onlyNoLayoutUpdates) {
  1425. Profiler.enter("Layout processing");
  1426. try {
  1427. LayoutManager layoutManager = getLayoutManager();
  1428. layoutManager.setEverythingNeedsMeasure();
  1429. layoutManager.layoutNow();
  1430. } catch (final Throwable e) {
  1431. VConsole.error(e);
  1432. }
  1433. Profiler.leave("Layout processing");
  1434. }
  1435. if (ApplicationConfiguration.isDebugMode()) {
  1436. Profiler.enter("Dumping state changes to the console");
  1437. VConsole.log(" * Dumping state changes to the console");
  1438. VConsole.dirUIDL(json, ApplicationConnection.this);
  1439. Profiler.leave("Dumping state changes to the console");
  1440. }
  1441. if (meta != null) {
  1442. Profiler.enter("Error handling");
  1443. if (meta.containsKey("appError")) {
  1444. ValueMap error = meta.getValueMap("appError");
  1445. showError(null, error.getString("caption"),
  1446. error.getString("message"),
  1447. error.getString("url"));
  1448. setApplicationRunning(false);
  1449. }
  1450. Profiler.leave("Error handling");
  1451. }
  1452. // TODO build profiling for widget impl loading time
  1453. lastProcessingTime = (int) ((new Date().getTime()) - start
  1454. .getTime());
  1455. totalProcessingTime += lastProcessingTime;
  1456. if (bootstrapTime == 0) {
  1457. bootstrapTime = calculateBootstrapTime();
  1458. if (Profiler.isEnabled() && bootstrapTime != -1) {
  1459. Profiler.logBootstrapTimings();
  1460. }
  1461. }
  1462. VConsole.log(" Processing time was "
  1463. + String.valueOf(lastProcessingTime) + "ms for "
  1464. + jsonText.length() + " characters of JSON");
  1465. VConsole.log("Referenced paintables: " + connectorMap.size());
  1466. if (meta == null || !meta.containsKey("async")) {
  1467. // End the request if the received message was a response,
  1468. // not sent asynchronously
  1469. endRequest();
  1470. }
  1471. resumeResponseHandling(lock);
  1472. if (Profiler.isEnabled()) {
  1473. Scheduler.get().scheduleDeferred(new ScheduledCommand() {
  1474. @Override
  1475. public void execute() {
  1476. Profiler.logTimings();
  1477. Profiler.reset();
  1478. }
  1479. });
  1480. }
  1481. }
  1482. /**
  1483. * Properly clean up any old stuff to ensure everything is properly
  1484. * reinitialized.
  1485. */
  1486. private void prepareRepaintAll() {
  1487. String uiConnectorId = uIConnector.getConnectorId();
  1488. if (uiConnectorId == null) {
  1489. // Nothing to clear yet
  1490. return;
  1491. }
  1492. // Create fake server response that says that the uiConnector
  1493. // has no children
  1494. JSONObject fakeHierarchy = new JSONObject();
  1495. fakeHierarchy.put(uiConnectorId, new JSONArray());
  1496. JSONObject fakeJson = new JSONObject();
  1497. fakeJson.put("hierarchy", fakeHierarchy);
  1498. ValueMap fakeValueMap = fakeJson.getJavaScriptObject().cast();
  1499. // Update hierarchy based on the fake response
  1500. ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(fakeValueMap);
  1501. // Send hierarchy events based on the fake update
  1502. sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events);
  1503. // Unregister all the old connectors that have now been removed
  1504. unregisterRemovedConnectors();
  1505. getLayoutManager().cleanMeasuredSizes();
  1506. }
  1507. private void updateCaptions(
  1508. JsArrayObject<StateChangeEvent> pendingStateChangeEvents,
  1509. FastStringSet parentChangedIds) {
  1510. Profiler.enter("updateCaptions");
  1511. /*
  1512. * Find all components that might need a caption update based on
  1513. * pending state and hierarchy changes
  1514. */
  1515. FastStringSet needsCaptionUpdate = FastStringSet.create();
  1516. needsCaptionUpdate.addAll(parentChangedIds);
  1517. // Find components with potentially changed caption state
  1518. int size = pendingStateChangeEvents.size();
  1519. for (int i = 0; i < size; i++) {
  1520. StateChangeEvent event = pendingStateChangeEvents.get(i);
  1521. if (VCaption.mightChange(event)) {
  1522. ServerConnector connector = event.getConnector();
  1523. needsCaptionUpdate.add(connector.getConnectorId());
  1524. }
  1525. }
  1526. ConnectorMap connectorMap = getConnectorMap();
  1527. // Update captions for all suitable candidates
  1528. JsArrayString dump = needsCaptionUpdate.dump();
  1529. int needsUpdateLength = dump.length();
  1530. for (int i = 0; i < needsUpdateLength; i++) {
  1531. String childId = dump.get(i);
  1532. ServerConnector child = connectorMap.getConnector(childId);
  1533. if (child instanceof ComponentConnector
  1534. && ((ComponentConnector) child)
  1535. .delegateCaptionHandling()) {
  1536. ServerConnector parent = child.getParent();
  1537. if (parent instanceof HasComponentsConnector) {
  1538. Profiler.enter("HasComponentsConnector.updateCaption");
  1539. ((HasComponentsConnector) parent)
  1540. .updateCaption((ComponentConnector) child);
  1541. Profiler.leave("HasComponentsConnector.updateCaption");
  1542. }
  1543. }
  1544. }
  1545. Profiler.leave("updateCaptions");
  1546. }
  1547. private void delegateToWidget(
  1548. JsArrayObject<StateChangeEvent> pendingStateChangeEvents) {
  1549. Profiler.enter("@DelegateToWidget");
  1550. VConsole.log(" * Running @DelegateToWidget");
  1551. // Keep track of types that have no @DelegateToWidget in their
  1552. // state to optimize performance
  1553. FastStringSet noOpTypes = FastStringSet.create();
  1554. int size = pendingStateChangeEvents.size();
  1555. for (int eventIndex = 0; eventIndex < size; eventIndex++) {
  1556. StateChangeEvent sce = pendingStateChangeEvents
  1557. .get(eventIndex);
  1558. ServerConnector connector = sce.getConnector();
  1559. if (connector instanceof ComponentConnector) {
  1560. String className = connector.getClass().getName();
  1561. if (noOpTypes.contains(className)) {
  1562. continue;
  1563. }
  1564. ComponentConnector component = (ComponentConnector) connector;
  1565. Type stateType = AbstractConnector
  1566. .getStateType(component);
  1567. JsArrayString delegateToWidgetProperties = stateType
  1568. .getDelegateToWidgetProperties();
  1569. if (delegateToWidgetProperties == null) {
  1570. noOpTypes.add(className);
  1571. continue;
  1572. }
  1573. int length = delegateToWidgetProperties.length();
  1574. for (int i = 0; i < length; i++) {
  1575. String propertyName = delegateToWidgetProperties
  1576. .get(i);
  1577. if (sce.hasPropertyChanged(propertyName)) {
  1578. Property property = stateType
  1579. .getProperty(propertyName);
  1580. String method = property
  1581. .getDelegateToWidgetMethodName();
  1582. Profiler.enter("doDelegateToWidget");
  1583. doDelegateToWidget(component, property, method);
  1584. Profiler.leave("doDelegateToWidget");
  1585. }
  1586. }
  1587. }
  1588. }
  1589. Profiler.leave("@DelegateToWidget");
  1590. }
  1591. private void doDelegateToWidget(ComponentConnector component,
  1592. Property property, String methodName) {
  1593. Type type = TypeData.getType(component.getClass());
  1594. try {
  1595. Type widgetType = type.getMethod("getWidget")
  1596. .getReturnType();
  1597. Widget widget = component.getWidget();
  1598. Object propertyValue = property.getValue(component
  1599. .getState());
  1600. widgetType.getMethod(methodName).invoke(widget,
  1601. propertyValue);
  1602. } catch (NoDataException e) {
  1603. throw new RuntimeException(
  1604. "Missing data needed to invoke @DelegateToWidget for "
  1605. + Util.getSimpleName(component), e);
  1606. }
  1607. }
  1608. /**
  1609. * Sends the state change events created while updating the state
  1610. * information.
  1611. *
  1612. * This must be called after hierarchy change listeners have been
  1613. * called. At least caption updates for the parent are strange if
  1614. * fired from state change listeners and thus calls the parent
  1615. * BEFORE the parent is aware of the child (through a
  1616. * ConnectorHierarchyChangedEvent)
  1617. *
  1618. * @param pendingStateChangeEvents
  1619. * The events to send
  1620. */
  1621. private void sendStateChangeEvents(
  1622. JsArrayObject<StateChangeEvent> pendingStateChangeEvents) {
  1623. Profiler.enter("sendStateChangeEvents");
  1624. VConsole.log(" * Sending state change events");
  1625. int size = pendingStateChangeEvents.size();
  1626. for (int i = 0; i < size; i++) {
  1627. StateChangeEvent sce = pendingStateChangeEvents.get(i);
  1628. try {
  1629. sce.getConnector().fireEvent(sce);
  1630. } catch (final Throwable e) {
  1631. VConsole.error(e);
  1632. }
  1633. }
  1634. Profiler.leave("sendStateChangeEvents");
  1635. }
  1636. private void unregisterRemovedConnectors() {
  1637. Profiler.enter("unregisterRemovedConnectors");
  1638. int unregistered = 0;
  1639. JsArrayObject<ServerConnector> currentConnectors = connectorMap
  1640. .getConnectorsAsJsArray();
  1641. int size = currentConnectors.size();
  1642. for (int i = 0; i < size; i++) {
  1643. ServerConnector c = currentConnectors.get(i);
  1644. if (c.getParent() != null) {
  1645. // only do this check if debug mode is active
  1646. if (ApplicationConfiguration.isDebugMode()) {
  1647. Profiler.enter("unregisterRemovedConnectors check parent - this is only performed in debug mode");
  1648. // this is slow for large layouts, 25-30% of total
  1649. // time for some operations even on modern browsers
  1650. if (!c.getParent().getChildren().contains(c)) {
  1651. VConsole.error("ERROR: Connector is connected to a parent but the parent does not contain the connector");
  1652. }
  1653. Profiler.leave("unregisterRemovedConnectors check parent - this is only performed in debug mode");
  1654. }
  1655. } else if (c == getUIConnector()) {
  1656. // UIConnector for this connection, leave as-is
  1657. } else if (c instanceof WindowConnector
  1658. && getUIConnector().hasSubWindow(
  1659. (WindowConnector) c)) {
  1660. // Sub window attached to this UIConnector, leave
  1661. // as-is
  1662. } else {
  1663. // The connector has been detached from the
  1664. // hierarchy, unregister it and any possible
  1665. // children. The UIConnector should never be
  1666. // unregistered even though it has no parent.
  1667. Profiler.enter("unregisterRemovedConnectors unregisterConnector");
  1668. connectorMap.unregisterConnector(c);
  1669. Profiler.leave("unregisterRemovedConnectors unregisterConnector");
  1670. unregistered++;
  1671. }
  1672. }
  1673. VConsole.log("* Unregistered " + unregistered + " connectors");
  1674. Profiler.leave("unregisterRemovedConnectors");
  1675. }
  1676. private JsArrayString createConnectorsIfNeeded(ValueMap json) {
  1677. VConsole.log(" * Creating connectors (if needed)");
  1678. JsArrayString createdConnectors = JavaScriptObject
  1679. .createArray().cast();
  1680. if (!json.containsKey("types")) {
  1681. return createdConnectors;
  1682. }
  1683. Profiler.enter("Creating connectors");
  1684. ValueMap types = json.getValueMap("types");
  1685. JsArrayString keyArray = types.getKeyArray();
  1686. for (int i = 0; i < keyArray.length(); i++) {
  1687. try {
  1688. String connectorId = keyArray.get(i);
  1689. ServerConnector connector = connectorMap
  1690. .getConnector(connectorId);
  1691. if (connector != null) {
  1692. continue;
  1693. }
  1694. // Always do layouts if there's at least one new
  1695. // connector
  1696. onlyNoLayoutUpdates = false;
  1697. int connectorType = Integer.parseInt(types
  1698. .getString(connectorId));
  1699. Class<? extends ServerConnector> connectorClass = configuration
  1700. .getConnectorClassByEncodedTag(connectorType);
  1701. // Connector does not exist so we must create it
  1702. if (connectorClass != uIConnector.getClass()) {
  1703. // create, initialize and register the paintable
  1704. Profiler.enter("ApplicationConnection.getConnector");
  1705. connector = getConnector(connectorId, connectorType);
  1706. Profiler.leave("ApplicationConnection.getConnector");
  1707. createdConnectors.push(connectorId);
  1708. } else {
  1709. // First UIConnector update. Before this the
  1710. // UIConnector has been created but not
  1711. // initialized as the connector id has not been
  1712. // known
  1713. connectorMap.registerConnector(connectorId,
  1714. uIConnector);
  1715. uIConnector.doInit(connectorId,
  1716. ApplicationConnection.this);
  1717. createdConnectors.push(connectorId);
  1718. }
  1719. } catch (final Throwable e) {
  1720. VConsole.error(e);
  1721. }
  1722. }
  1723. Profiler.leave("Creating connectors");
  1724. return createdConnectors;
  1725. }
  1726. private void updateVaadin6StyleConnectors(ValueMap json) {
  1727. Profiler.enter("updateVaadin6StyleConnectors");
  1728. JsArray<ValueMap> changes = json.getJSValueMapArray("changes");
  1729. int length = changes.length();
  1730. // Must always do layout if there's even a single legacy update
  1731. if (length != 0) {
  1732. onlyNoLayoutUpdates = false;
  1733. }
  1734. VConsole.log(" * Passing UIDL to Vaadin 6 style connectors");
  1735. // update paintables
  1736. for (int i = 0; i < length; i++) {
  1737. try {
  1738. final UIDL change = changes.get(i).cast();
  1739. final UIDL uidl = change.getChildUIDL(0);
  1740. String connectorId = uidl.getId();
  1741. final ComponentConnector legacyConnector = (ComponentConnector) connectorMap
  1742. .getConnector(connectorId);
  1743. if (legacyConnector instanceof Paintable) {
  1744. String key = null;
  1745. if (Profiler.isEnabled()) {
  1746. key = "updateFromUIDL for "
  1747. + Util.getSimpleName(legacyConnector);
  1748. Profiler.enter(key);
  1749. }
  1750. ((Paintable) legacyConnector).updateFromUIDL(uidl,
  1751. ApplicationConnection.this);
  1752. if (Profiler.isEnabled()) {
  1753. Profiler.leave(key);
  1754. }
  1755. } else if (legacyConnector == null) {
  1756. VConsole.error("Received update for "
  1757. + uidl.getTag()
  1758. + ", but there is no such paintable ("
  1759. + connectorId + ") rendered.");
  1760. } else {
  1761. VConsole.error("Server sent Vaadin 6 style updates for "
  1762. + Util.getConnectorString(legacyConnector)
  1763. + " but this is not a Vaadin 6 Paintable");
  1764. }
  1765. } catch (final Throwable e) {
  1766. VConsole.error(e);
  1767. }
  1768. }
  1769. Profiler.leave("updateVaadin6StyleConnectors");
  1770. }
  1771. private void sendHierarchyChangeEvents(
  1772. JsArrayObject<ConnectorHierarchyChangeEvent> events) {
  1773. int eventCount = events.size();
  1774. if (eventCount == 0) {
  1775. return;
  1776. }
  1777. Profiler.enter("sendHierarchyChangeEvents");
  1778. VConsole.log(" * Sending hierarchy change events");
  1779. for (int i = 0; i < eventCount; i++) {
  1780. ConnectorHierarchyChangeEvent event = events.get(i);
  1781. try {
  1782. logHierarchyChange(event);
  1783. event.getConnector().fireEvent(event);
  1784. } catch (final Throwable e) {
  1785. VConsole.error(e);
  1786. }
  1787. }
  1788. Profiler.leave("sendHierarchyChangeEvents");
  1789. }
  1790. private void logHierarchyChange(ConnectorHierarchyChangeEvent event) {
  1791. if (true) {
  1792. // Always disabled for now. Can be enabled manually
  1793. return;
  1794. }
  1795. VConsole.log("Hierarchy changed for "
  1796. + Util.getConnectorString(event.getConnector()));
  1797. String oldChildren = "* Old children: ";
  1798. for (ComponentConnector child : event.getOldChildren()) {
  1799. oldChildren += Util.getConnectorString(child) + " ";
  1800. }
  1801. VConsole.log(oldChildren);
  1802. String newChildren = "* New children: ";
  1803. HasComponentsConnector parent = (HasComponentsConnector) event
  1804. .getConnector();
  1805. for (ComponentConnector child : parent.getChildComponents()) {
  1806. newChildren += Util.getConnectorString(child) + " ";
  1807. }
  1808. VConsole.log(newChildren);
  1809. }
  1810. private JsArrayObject<StateChangeEvent> updateConnectorState(
  1811. ValueMap json, JsArrayString createdConnectorIds) {
  1812. JsArrayObject<StateChangeEvent> events = JavaScriptObject
  1813. .createArray().cast();
  1814. VConsole.log(" * Updating connector states");
  1815. if (!json.containsKey("state")) {
  1816. return events;
  1817. }
  1818. Profiler.enter("updateConnectorState");
  1819. FastStringSet remainingNewConnectors = FastStringSet.create();
  1820. remainingNewConnectors.addAll(createdConnectorIds);
  1821. // set states for all paintables mentioned in "state"
  1822. ValueMap states = json.getValueMap("state");
  1823. JsArrayString keyArray = states.getKeyArray();
  1824. for (int i = 0; i < keyArray.length(); i++) {
  1825. try {
  1826. String connectorId = keyArray.get(i);
  1827. ServerConnector connector = connectorMap
  1828. .getConnector(connectorId);
  1829. if (null != connector) {
  1830. Profiler.enter("updateConnectorState inner loop");
  1831. if (Profiler.isEnabled()) {
  1832. Profiler.enter("Decode connector state "
  1833. + Util.getSimpleName(connector));
  1834. }
  1835. JSONObject stateJson = new JSONObject(
  1836. states.getJavaScriptObject(connectorId));
  1837. if (connector instanceof HasJavaScriptConnectorHelper) {
  1838. ((HasJavaScriptConnectorHelper) connector)
  1839. .getJavascriptConnectorHelper()
  1840. .setNativeState(
  1841. stateJson.getJavaScriptObject());
  1842. }
  1843. SharedState state = connector.getState();
  1844. Type stateType = new Type(state.getClass()
  1845. .getName(), null);
  1846. if (onlyNoLayoutUpdates) {
  1847. Profiler.enter("updateConnectorState @NoLayout handling");
  1848. Set<String> keySet = stateJson.keySet();
  1849. for (String propertyName : keySet) {
  1850. Property property = stateType
  1851. .getProperty(propertyName);
  1852. if (!property.isNoLayout()) {
  1853. onlyNoLayoutUpdates = false;
  1854. break;
  1855. }
  1856. }
  1857. Profiler.leave("updateConnectorState @NoLayout handling");
  1858. }
  1859. Profiler.enter("updateConnectorState decodeValue");
  1860. JsonDecoder.decodeValue(stateType, stateJson,
  1861. state, ApplicationConnection.this);
  1862. Profiler.leave("updateConnectorState decodeValue");
  1863. if (Profiler.isEnabled()) {
  1864. Profiler.leave("Decode connector state "
  1865. + Util.getSimpleName(connector));
  1866. }
  1867. Profiler.enter("updateConnectorState create event");
  1868. boolean isNewConnector = remainingNewConnectors
  1869. .contains(connectorId);
  1870. if (isNewConnector) {
  1871. remainingNewConnectors.remove(connectorId);
  1872. }
  1873. StateChangeEvent event = new StateChangeEvent(
  1874. connector, stateJson, isNewConnector);
  1875. events.add(event);
  1876. Profiler.leave("updateConnectorState create event");
  1877. Profiler.leave("updateConnectorState inner loop");
  1878. }
  1879. } catch (final Throwable e) {
  1880. VConsole.error(e);
  1881. }
  1882. }
  1883. Profiler.enter("updateConnectorState newWithoutState");
  1884. // Fire events for properties using the default value for newly
  1885. // created connectors even if there were no state changes
  1886. JsArrayString dump = remainingNewConnectors.dump();
  1887. int length = dump.length();
  1888. for (int i = 0; i < length; i++) {
  1889. String connectorId = dump.get(i);
  1890. ServerConnector connector = connectorMap
  1891. .getConnector(connectorId);
  1892. StateChangeEvent event = new StateChangeEvent(connector,
  1893. new JSONObject(), true);
  1894. events.add(event);
  1895. }
  1896. Profiler.leave("updateConnectorState newWithoutState");
  1897. Profiler.leave("updateConnectorState");
  1898. return events;
  1899. }
  1900. /**
  1901. * Updates the connector hierarchy and returns a list of events that
  1902. * should be fired after update of the hierarchy and the state is
  1903. * done.
  1904. *
  1905. * @param json
  1906. * The JSON containing the hierarchy information
  1907. * @return A collection of events that should be fired when update
  1908. * of hierarchy and state is complete and a list of all
  1909. * connectors for which the parent has changed
  1910. */
  1911. private ConnectorHierarchyUpdateResult updateConnectorHierarchy(
  1912. ValueMap json) {
  1913. ConnectorHierarchyUpdateResult result = new ConnectorHierarchyUpdateResult();
  1914. VConsole.log(" * Updating connector hierarchy");
  1915. if (!json.containsKey("hierarchy")) {
  1916. return result;
  1917. }
  1918. Profiler.enter("updateConnectorHierarchy");
  1919. FastStringSet maybeDetached = FastStringSet.create();
  1920. ValueMap hierarchies = json.getValueMap("hierarchy");
  1921. JsArrayString hierarchyKeys = hierarchies.getKeyArray();
  1922. for (int i = 0; i < hierarchyKeys.length(); i++) {
  1923. try {
  1924. Profiler.enter("updateConnectorHierarchy hierarchy entry");
  1925. String connectorId = hierarchyKeys.get(i);
  1926. ServerConnector parentConnector = connectorMap
  1927. .getConnector(connectorId);
  1928. JsArrayString childConnectorIds = hierarchies
  1929. .getJSStringArray(connectorId);
  1930. int childConnectorSize = childConnectorIds.length();
  1931. Profiler.enter("updateConnectorHierarchy find new connectors");
  1932. List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
  1933. List<ComponentConnector> newComponents = new ArrayList<ComponentConnector>();
  1934. for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
  1935. String childConnectorId = childConnectorIds
  1936. .get(connectorIndex);
  1937. ServerConnector childConnector = connectorMap
  1938. .getConnector(childConnectorId);
  1939. if (childConnector == null) {
  1940. VConsole.error("Hierarchy claims that "
  1941. + childConnectorId
  1942. + " is a child for "
  1943. + connectorId
  1944. + " ("
  1945. + parentConnector.getClass().getName()
  1946. + ") but no connector with id "
  1947. + childConnectorId
  1948. + " has been registered. "
  1949. + "More information might be available in the server-side log if assertions are enabled");
  1950. continue;
  1951. }
  1952. newChildren.add(childConnector);
  1953. if (childConnector instanceof ComponentConnector) {
  1954. newComponents
  1955. .add((ComponentConnector) childConnector);
  1956. } else if (!(childConnector instanceof AbstractExtensionConnector)) {
  1957. throw new IllegalStateException(
  1958. Util.getConnectorString(childConnector)
  1959. + " is not a ComponentConnector nor an AbstractExtensionConnector");
  1960. }
  1961. if (childConnector.getParent() != parentConnector) {
  1962. childConnector.setParent(parentConnector);
  1963. result.parentChangedIds.add(childConnectorId);
  1964. // Not detached even if previously removed from
  1965. // parent
  1966. maybeDetached.remove(childConnectorId);
  1967. }
  1968. }
  1969. Profiler.leave("updateConnectorHierarchy find new connectors");
  1970. // TODO This check should be done on the server side in
  1971. // the future so the hierarchy update is only sent when
  1972. // something actually has changed
  1973. List<ServerConnector> oldChildren = parentConnector
  1974. .getChildren();
  1975. boolean actuallyChanged = !Util.collectionsEquals(
  1976. oldChildren, newChildren);
  1977. if (!actuallyChanged) {
  1978. continue;
  1979. }
  1980. Profiler.enter("updateConnectorHierarchy handle HasComponentsConnector");
  1981. if (parentConnector instanceof HasComponentsConnector) {
  1982. HasComponentsConnector ccc = (HasComponentsConnector) parentConnector;
  1983. List<ComponentConnector> oldComponents = ccc
  1984. .getChildComponents();
  1985. if (!Util.collectionsEquals(oldComponents,
  1986. newComponents)) {
  1987. // Fire change event if the hierarchy has
  1988. // changed
  1989. ConnectorHierarchyChangeEvent event = GWT
  1990. .create(ConnectorHierarchyChangeEvent.class);
  1991. event.setOldChildren(oldComponents);
  1992. event.setConnector(parentConnector);
  1993. ccc.setChildComponents(newComponents);
  1994. result.events.add(event);
  1995. }
  1996. } else if (!newComponents.isEmpty()) {
  1997. VConsole.error("Hierachy claims "
  1998. + Util.getConnectorString(parentConnector)
  1999. + " has component children even though it isn't a HasComponentsConnector");
  2000. }
  2001. Profiler.leave("updateConnectorHierarchy handle HasComponentsConnector");
  2002. Profiler.enter("updateConnectorHierarchy setChildren");
  2003. parentConnector.setChildren(newChildren);
  2004. Profiler.leave("updateConnectorHierarchy setChildren");
  2005. Profiler.enter("updateConnectorHierarchy find removed children");
  2006. /*
  2007. * Find children removed from this parent and mark for
  2008. * removal unless they are already attached to some
  2009. * other parent.
  2010. */
  2011. for (ServerConnector oldChild : oldChildren) {
  2012. if (oldChild.getParent() != parentConnector) {
  2013. // Ignore if moved to some other connector
  2014. continue;
  2015. }
  2016. if (!newChildren.contains(oldChild)) {
  2017. /*
  2018. * Consider child detached for now, will be
  2019. * cleared if it is later on added to some other
  2020. * parent.
  2021. */
  2022. maybeDetached.add(oldChild.getConnectorId());
  2023. }
  2024. }
  2025. Profiler.leave("updateConnectorHierarchy find removed children");
  2026. } catch (final Throwable e) {
  2027. VConsole.error(e);
  2028. } finally {
  2029. Profiler.leave("updateConnectorHierarchy hierarchy entry");
  2030. }
  2031. }
  2032. Profiler.enter("updateConnectorHierarchy detach removed connectors");
  2033. /*
  2034. * Connector is in maybeDetached at this point if it has been
  2035. * removed from its parent but not added to any other parent
  2036. */
  2037. JsArrayString maybeDetachedArray = maybeDetached.dump();
  2038. for (int i = 0; i < maybeDetachedArray.length(); i++) {
  2039. ServerConnector removed = connectorMap
  2040. .getConnector(maybeDetachedArray.get(i));
  2041. recursivelyDetach(removed, result.events);
  2042. }
  2043. Profiler.leave("updateConnectorHierarchy detach removed connectors");
  2044. if (result.events.size() != 0) {
  2045. onlyNoLayoutUpdates = false;
  2046. }
  2047. Profiler.leave("updateConnectorHierarchy");
  2048. return result;
  2049. }
  2050. private void recursivelyDetach(ServerConnector connector,
  2051. JsArrayObject<ConnectorHierarchyChangeEvent> events) {
  2052. /*
  2053. * Reset state in an attempt to keep it consistent with the
  2054. * hierarchy. No children and no parent is the initial situation
  2055. * for the hierarchy, so changing the state to its initial value
  2056. * is the closest we can get without data from the server.
  2057. * #10151
  2058. */
  2059. Profiler.enter("ApplicationConnection recursivelyDetach reset state");
  2060. try {
  2061. Profiler.enter("ApplicationConnection recursivelyDetach reset state - getStateType");
  2062. Type stateType = AbstractConnector.getStateType(connector);
  2063. Profiler.leave("ApplicationConnection recursivelyDetach reset state - getStateType");
  2064. // Empty state instance to get default property values from
  2065. Profiler.enter("ApplicationConnection recursivelyDetach reset state - createInstance");
  2066. Object defaultState = stateType.createInstance();
  2067. Profiler.leave("ApplicationConnection recursivelyDetach reset state - createInstance");
  2068. if (connector instanceof AbstractConnector) {
  2069. // optimization as the loop setting properties is very
  2070. // slow, especially on IE8
  2071. replaceState((AbstractConnector) connector,
  2072. defaultState);
  2073. } else {
  2074. SharedState state = connector.getState();
  2075. Profiler.enter("ApplicationConnection recursivelyDetach reset state - properties");
  2076. JsArrayObject<Property> properties = stateType
  2077. .getPropertiesAsArray();
  2078. int size = properties.size();
  2079. for (int i = 0; i < size; i++) {
  2080. Property property = properties.get(i);
  2081. property.setValue(state,
  2082. property.getValue(defaultState));
  2083. }
  2084. Profiler.leave("ApplicationConnection recursivelyDetach reset state - properties");
  2085. }
  2086. } catch (NoDataException e) {
  2087. throw new RuntimeException("Can't reset state for "
  2088. + Util.getConnectorString(connector), e);
  2089. } finally {
  2090. Profiler.leave("ApplicationConnection recursivelyDetach reset state");
  2091. }
  2092. Profiler.enter("ApplicationConnection recursivelyDetach perform detach");
  2093. /*
  2094. * Recursively detach children to make sure they get
  2095. * setParent(null) and hierarchy change events as needed.
  2096. */
  2097. for (ServerConnector child : connector.getChildren()) {
  2098. /*
  2099. * Server doesn't send updated child data for removed
  2100. * connectors -> ignore child that still seems to be a child
  2101. * of this connector although it has been moved to some part
  2102. * of the hierarchy that is not detached.
  2103. */
  2104. if (child.getParent() != connector) {
  2105. continue;
  2106. }
  2107. recursivelyDetach(child, events);
  2108. }
  2109. Profiler.leave("ApplicationConnection recursivelyDetach perform detach");
  2110. /*
  2111. * Clear child list and parent
  2112. */
  2113. Profiler.enter("ApplicationConnection recursivelyDetach clear children and parent");
  2114. connector
  2115. .setChildren(Collections.<ServerConnector> emptyList());
  2116. connector.setParent(null);
  2117. Profiler.leave("ApplicationConnection recursivelyDetach clear children and parent");
  2118. /*
  2119. * Create an artificial hierarchy event for containers to give
  2120. * it a chance to clean up after its children if it has any
  2121. */
  2122. Profiler.enter("ApplicationConnection recursivelyDetach create hierarchy event");
  2123. if (connector instanceof HasComponentsConnector) {
  2124. HasComponentsConnector ccc = (HasComponentsConnector) connector;
  2125. List<ComponentConnector> oldChildren = ccc
  2126. .getChildComponents();
  2127. if (!oldChildren.isEmpty()) {
  2128. /*
  2129. * HasComponentsConnector has a separate child component
  2130. * list that should also be cleared
  2131. */
  2132. ccc.setChildComponents(Collections
  2133. .<ComponentConnector> emptyList());
  2134. // Create event and add it to the list of pending events
  2135. ConnectorHierarchyChangeEvent event = GWT
  2136. .create(ConnectorHierarchyChangeEvent.class);
  2137. event.setConnector(connector);
  2138. event.setOldChildren(oldChildren);
  2139. events.add(event);
  2140. }
  2141. }
  2142. Profiler.leave("ApplicationConnection recursivelyDetach create hierarchy event");
  2143. }
  2144. private native void replaceState(AbstractConnector connector,
  2145. Object defaultState)
  2146. /*-{
  2147. connector.@com.vaadin.client.ui.AbstractConnector::state = defaultState;
  2148. }-*/;
  2149. private void handleRpcInvocations(ValueMap json) {
  2150. if (json.containsKey("rpc")) {
  2151. Profiler.enter("handleRpcInvocations");
  2152. VConsole.log(" * Performing server to client RPC calls");
  2153. JSONArray rpcCalls = new JSONArray(
  2154. json.getJavaScriptObject("rpc"));
  2155. int rpcLength = rpcCalls.size();
  2156. for (int i = 0; i < rpcLength; i++) {
  2157. try {
  2158. JSONArray rpcCall = (JSONArray) rpcCalls.get(i);
  2159. MethodInvocation invocation = rpcManager
  2160. .parseAndApplyInvocation(rpcCall,
  2161. ApplicationConnection.this);
  2162. if (onlyNoLayoutUpdates
  2163. && !RpcManager.getMethod(invocation)
  2164. .isNoLayout()) {
  2165. onlyNoLayoutUpdates = false;
  2166. }
  2167. } catch (final Throwable e) {
  2168. VConsole.error(e);
  2169. }
  2170. }
  2171. Profiler.leave("handleRpcInvocations");
  2172. }
  2173. }
  2174. };
  2175. ApplicationConfiguration.runWhenDependenciesLoaded(c);
  2176. }
  2177. private void loadStyleDependencies(JsArrayString dependencies) {
  2178. // Assuming no reason to interpret in a defined order
  2179. ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
  2180. @Override
  2181. public void onLoad(ResourceLoadEvent event) {
  2182. ApplicationConfiguration.endDependencyLoading();
  2183. }
  2184. @Override
  2185. public void onError(ResourceLoadEvent event) {
  2186. VConsole.error(event.getResourceUrl()
  2187. + " could not be loaded, or the load detection failed because the stylesheet is empty.");
  2188. // The show must go on
  2189. onLoad(event);
  2190. }
  2191. };
  2192. ResourceLoader loader = ResourceLoader.get();
  2193. for (int i = 0; i < dependencies.length(); i++) {
  2194. String url = translateVaadinUri(dependencies.get(i));
  2195. ApplicationConfiguration.startDependencyLoading();
  2196. loader.loadStylesheet(url, resourceLoadListener);
  2197. }
  2198. }
  2199. private void loadScriptDependencies(final JsArrayString dependencies) {
  2200. if (dependencies.length() == 0) {
  2201. return;
  2202. }
  2203. // Listener that loads the next when one is completed
  2204. ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
  2205. @Override
  2206. public void onLoad(ResourceLoadEvent event) {
  2207. if (dependencies.length() != 0) {
  2208. String url = translateVaadinUri(dependencies.shift());
  2209. ApplicationConfiguration.startDependencyLoading();
  2210. // Load next in chain (hopefully already preloaded)
  2211. event.getResourceLoader().loadScript(url, this);
  2212. }
  2213. // Call start for next before calling end for current
  2214. ApplicationConfiguration.endDependencyLoading();
  2215. }
  2216. @Override
  2217. public void onError(ResourceLoadEvent event) {
  2218. VConsole.error(event.getResourceUrl() + " could not be loaded.");
  2219. // The show must go on
  2220. onLoad(event);
  2221. }
  2222. };
  2223. ResourceLoader loader = ResourceLoader.get();
  2224. // Start chain by loading first
  2225. String url = translateVaadinUri(dependencies.shift());
  2226. ApplicationConfiguration.startDependencyLoading();
  2227. loader.loadScript(url, resourceLoadListener);
  2228. if (ResourceLoader.supportsInOrderScriptExecution()) {
  2229. for (int i = 0; i < dependencies.length(); i++) {
  2230. String preloadUrl = translateVaadinUri(dependencies.get(i));
  2231. loader.loadScript(preloadUrl, null);
  2232. }
  2233. } else {
  2234. // Preload all remaining
  2235. for (int i = 0; i < dependencies.length(); i++) {
  2236. String preloadUrl = translateVaadinUri(dependencies.get(i));
  2237. loader.preloadResource(preloadUrl, null);
  2238. }
  2239. }
  2240. }
  2241. // Redirect browser, null reloads current page
  2242. private static native void redirect(String url)
  2243. /*-{
  2244. if (url) {
  2245. $wnd.location = url;
  2246. } else {
  2247. $wnd.location.reload(false);
  2248. }
  2249. }-*/;
  2250. private void addVariableToQueue(String connectorId, String variableName,
  2251. Object value, boolean immediate) {
  2252. boolean lastOnly = !immediate;
  2253. // note that type is now deduced from value
  2254. addMethodInvocationToQueue(new LegacyChangeVariablesInvocation(
  2255. connectorId, variableName, value), lastOnly, lastOnly);
  2256. }
  2257. /**
  2258. * Adds an explicit RPC method invocation to the send queue.
  2259. *
  2260. * @since 7.0
  2261. *
  2262. * @param invocation
  2263. * RPC method invocation
  2264. * @param delayed
  2265. * <code>false</code> to trigger sending within a short time
  2266. * window (possibly combining subsequent calls to a single
  2267. * request), <code>true</code> to let the framework delay sending
  2268. * of RPC calls and variable changes until the next non-delayed
  2269. * change
  2270. * @param lastOnly
  2271. * <code>true</code> to remove all previously delayed invocations
  2272. * of the same method that were also enqueued with lastonly set
  2273. * to <code>true</code>. <code>false</code> to add invocation to
  2274. * the end of the queue without touching previously enqueued
  2275. * invocations.
  2276. */
  2277. public void addMethodInvocationToQueue(MethodInvocation invocation,
  2278. boolean delayed, boolean lastOnly) {
  2279. if (!isApplicationRunning()) {
  2280. getLogger()
  2281. .warning(
  2282. "Trying to invoke method on not yet started or stopped application");
  2283. return;
  2284. }
  2285. String tag;
  2286. if (lastOnly) {
  2287. tag = invocation.getLastOnlyTag();
  2288. assert !tag.matches("\\d+") : "getLastOnlyTag value must have at least one non-digit character";
  2289. pendingInvocations.remove(tag);
  2290. } else {
  2291. tag = Integer.toString(lastInvocationTag++);
  2292. }
  2293. pendingInvocations.put(tag, invocation);
  2294. if (!delayed) {
  2295. sendPendingVariableChanges();
  2296. }
  2297. }
  2298. /**
  2299. * Removes any pending invocation of the given method from the queue
  2300. *
  2301. * @param invocation
  2302. * The invocation to remove
  2303. */
  2304. public void removePendingInvocations(MethodInvocation invocation) {
  2305. Iterator<MethodInvocation> iter = pendingInvocations.values()
  2306. .iterator();
  2307. while (iter.hasNext()) {
  2308. MethodInvocation mi = iter.next();
  2309. if (mi.equals(invocation)) {
  2310. iter.remove();
  2311. }
  2312. }
  2313. }
  2314. /**
  2315. * This method sends currently queued variable changes to server. It is
  2316. * called when immediate variable update must happen.
  2317. *
  2318. * To ensure correct order for variable changes (due servers multithreading
  2319. * or network), we always wait for active request to be handler before
  2320. * sending a new one. If there is an active request, we will put varible
  2321. * "burst" to queue that will be purged after current request is handled.
  2322. *
  2323. */
  2324. public void sendPendingVariableChanges() {
  2325. if (!deferedSendPending) {
  2326. deferedSendPending = true;
  2327. Scheduler.get().scheduleFinally(sendPendingCommand);
  2328. }
  2329. }
  2330. private final ScheduledCommand sendPendingCommand = new ScheduledCommand() {
  2331. @Override
  2332. public void execute() {
  2333. deferedSendPending = false;
  2334. doSendPendingVariableChanges();
  2335. }
  2336. };
  2337. private boolean deferedSendPending = false;
  2338. private void doSendPendingVariableChanges() {
  2339. if (isApplicationRunning()) {
  2340. if (hasActiveRequest() || (push != null && !push.isActive())) {
  2341. // skip empty queues if there are pending bursts to be sent
  2342. if (pendingInvocations.size() > 0 || pendingBursts.size() == 0) {
  2343. pendingBursts.add(pendingInvocations);
  2344. pendingInvocations = new LinkedHashMap<String, MethodInvocation>();
  2345. // Keep tag string short
  2346. lastInvocationTag = 0;
  2347. }
  2348. } else {
  2349. buildAndSendVariableBurst(pendingInvocations);
  2350. }
  2351. } else {
  2352. getLogger()
  2353. .warning(
  2354. "Trying to send variable changes from not yet started or stopped application");
  2355. return;
  2356. }
  2357. }
  2358. /**
  2359. * Build the variable burst and send it to server.
  2360. *
  2361. * When sync is forced, we also force sending of all pending variable-bursts
  2362. * at the same time. This is ok as we can assume that DOM will never be
  2363. * updated after this.
  2364. *
  2365. * @param pendingInvocations
  2366. * List of RPC method invocations to send
  2367. */
  2368. private void buildAndSendVariableBurst(
  2369. LinkedHashMap<String, MethodInvocation> pendingInvocations) {
  2370. JSONArray reqJson = new JSONArray();
  2371. if (!pendingInvocations.isEmpty()) {
  2372. if (ApplicationConfiguration.isDebugMode()) {
  2373. Util.logVariableBurst(this, pendingInvocations.values());
  2374. }
  2375. for (MethodInvocation invocation : pendingInvocations.values()) {
  2376. JSONArray invocationJson = new JSONArray();
  2377. invocationJson.set(0,
  2378. new JSONString(invocation.getConnectorId()));
  2379. invocationJson.set(1,
  2380. new JSONString(invocation.getInterfaceName()));
  2381. invocationJson.set(2,
  2382. new JSONString(invocation.getMethodName()));
  2383. JSONArray paramJson = new JSONArray();
  2384. Type[] parameterTypes = null;
  2385. if (!isLegacyVariableChange(invocation)
  2386. && !isJavascriptRpc(invocation)) {
  2387. try {
  2388. Type type = new Type(invocation.getInterfaceName(),
  2389. null);
  2390. Method method = type.getMethod(invocation
  2391. .getMethodName());
  2392. parameterTypes = method.getParameterTypes();
  2393. } catch (NoDataException e) {
  2394. throw new RuntimeException("No type data for "
  2395. + invocation.toString(), e);
  2396. }
  2397. }
  2398. for (int i = 0; i < invocation.getParameters().length; ++i) {
  2399. // TODO non-static encoder?
  2400. Type type = null;
  2401. if (parameterTypes != null) {
  2402. type = parameterTypes[i];
  2403. }
  2404. Object value = invocation.getParameters()[i];
  2405. paramJson.set(i, JsonEncoder.encode(value, type, this));
  2406. }
  2407. invocationJson.set(3, paramJson);
  2408. reqJson.set(reqJson.size(), invocationJson);
  2409. }
  2410. pendingInvocations.clear();
  2411. // Keep tag string short
  2412. lastInvocationTag = 0;
  2413. }
  2414. String extraParams = "";
  2415. if (!getConfiguration().isWidgetsetVersionSent()) {
  2416. if (!extraParams.isEmpty()) {
  2417. extraParams += "&";
  2418. }
  2419. String widgetsetVersion = Version.getFullVersion();
  2420. extraParams += "v-wsver=" + widgetsetVersion;
  2421. getConfiguration().setWidgetsetVersionSent();
  2422. }
  2423. makeUidlRequest(reqJson, extraParams);
  2424. }
  2425. private boolean isJavascriptRpc(MethodInvocation invocation) {
  2426. return invocation instanceof JavaScriptMethodInvocation;
  2427. }
  2428. private boolean isLegacyVariableChange(MethodInvocation invocation) {
  2429. return ApplicationConstants.UPDATE_VARIABLE_METHOD.equals(invocation
  2430. .getInterfaceName())
  2431. && ApplicationConstants.UPDATE_VARIABLE_METHOD
  2432. .equals(invocation.getMethodName());
  2433. }
  2434. /**
  2435. * Sends a new value for the given paintables given variable to the server.
  2436. * <p>
  2437. * The update is actually queued to be sent at a suitable time. If immediate
  2438. * is true, the update is sent as soon as possible. If immediate is false,
  2439. * the update will be sent along with the next immediate update.
  2440. * </p>
  2441. *
  2442. * @param paintableId
  2443. * the id of the paintable that owns the variable
  2444. * @param variableName
  2445. * the name of the variable
  2446. * @param newValue
  2447. * the new value to be sent
  2448. * @param immediate
  2449. * true if the update is to be sent as soon as possible
  2450. */
  2451. public void updateVariable(String paintableId, String variableName,
  2452. ServerConnector newValue, boolean immediate) {
  2453. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2454. }
  2455. /**
  2456. * Sends a new value for the given paintables given variable to the server.
  2457. * <p>
  2458. * The update is actually queued to be sent at a suitable time. If immediate
  2459. * is true, the update is sent as soon as possible. If immediate is false,
  2460. * the update will be sent along with the next immediate update.
  2461. * </p>
  2462. *
  2463. * @param paintableId
  2464. * the id of the paintable that owns the variable
  2465. * @param variableName
  2466. * the name of the variable
  2467. * @param newValue
  2468. * the new value to be sent
  2469. * @param immediate
  2470. * true if the update is to be sent as soon as possible
  2471. */
  2472. public void updateVariable(String paintableId, String variableName,
  2473. String newValue, boolean immediate) {
  2474. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2475. }
  2476. /**
  2477. * Sends a new value for the given paintables given variable to the server.
  2478. * <p>
  2479. * The update is actually queued to be sent at a suitable time. If immediate
  2480. * is true, the update is sent as soon as possible. If immediate is false,
  2481. * the update will be sent along with the next immediate update.
  2482. * </p>
  2483. *
  2484. * @param paintableId
  2485. * the id of the paintable that owns the variable
  2486. * @param variableName
  2487. * the name of the variable
  2488. * @param newValue
  2489. * the new value to be sent
  2490. * @param immediate
  2491. * true if the update is to be sent as soon as possible
  2492. */
  2493. public void updateVariable(String paintableId, String variableName,
  2494. int newValue, boolean immediate) {
  2495. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2496. }
  2497. /**
  2498. * Sends a new value for the given paintables given variable to the server.
  2499. * <p>
  2500. * The update is actually queued to be sent at a suitable time. If immediate
  2501. * is true, the update is sent as soon as possible. If immediate is false,
  2502. * the update will be sent along with the next immediate update.
  2503. * </p>
  2504. *
  2505. * @param paintableId
  2506. * the id of the paintable that owns the variable
  2507. * @param variableName
  2508. * the name of the variable
  2509. * @param newValue
  2510. * the new value to be sent
  2511. * @param immediate
  2512. * true if the update is to be sent as soon as possible
  2513. */
  2514. public void updateVariable(String paintableId, String variableName,
  2515. long newValue, boolean immediate) {
  2516. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2517. }
  2518. /**
  2519. * Sends a new value for the given paintables given variable to the server.
  2520. * <p>
  2521. * The update is actually queued to be sent at a suitable time. If immediate
  2522. * is true, the update is sent as soon as possible. If immediate is false,
  2523. * the update will be sent along with the next immediate update.
  2524. * </p>
  2525. *
  2526. * @param paintableId
  2527. * the id of the paintable that owns the variable
  2528. * @param variableName
  2529. * the name of the variable
  2530. * @param newValue
  2531. * the new value to be sent
  2532. * @param immediate
  2533. * true if the update is to be sent as soon as possible
  2534. */
  2535. public void updateVariable(String paintableId, String variableName,
  2536. float newValue, boolean immediate) {
  2537. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2538. }
  2539. /**
  2540. * Sends a new value for the given paintables given variable to the server.
  2541. * <p>
  2542. * The update is actually queued to be sent at a suitable time. If immediate
  2543. * is true, the update is sent as soon as possible. If immediate is false,
  2544. * the update will be sent along with the next immediate update.
  2545. * </p>
  2546. *
  2547. * @param paintableId
  2548. * the id of the paintable that owns the variable
  2549. * @param variableName
  2550. * the name of the variable
  2551. * @param newValue
  2552. * the new value to be sent
  2553. * @param immediate
  2554. * true if the update is to be sent as soon as possible
  2555. */
  2556. public void updateVariable(String paintableId, String variableName,
  2557. double newValue, boolean immediate) {
  2558. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2559. }
  2560. /**
  2561. * Sends a new value for the given paintables given variable to the server.
  2562. * <p>
  2563. * The update is actually queued to be sent at a suitable time. If immediate
  2564. * is true, the update is sent as soon as possible. If immediate is false,
  2565. * the update will be sent along with the next immediate update.
  2566. * </p>
  2567. *
  2568. * @param paintableId
  2569. * the id of the paintable that owns the variable
  2570. * @param variableName
  2571. * the name of the variable
  2572. * @param newValue
  2573. * the new value to be sent
  2574. * @param immediate
  2575. * true if the update is to be sent as soon as possible
  2576. */
  2577. public void updateVariable(String paintableId, String variableName,
  2578. boolean newValue, boolean immediate) {
  2579. addVariableToQueue(paintableId, variableName, newValue, immediate);
  2580. }
  2581. /**
  2582. * Sends a new value for the given paintables given variable to the server.
  2583. * <p>
  2584. * The update is actually queued to be sent at a suitable time. If immediate
  2585. * is true, the update is sent as soon as possible. If immediate is false,
  2586. * the update will be sent along with the next immediate update.
  2587. * </p>
  2588. *
  2589. * @param paintableId
  2590. * the id of the paintable that owns the variable
  2591. * @param variableName
  2592. * the name of the variable
  2593. * @param map
  2594. * the new values to be sent
  2595. * @param immediate
  2596. * true if the update is to be sent as soon as possible
  2597. */
  2598. public void updateVariable(String paintableId, String variableName,
  2599. Map<String, Object> map, boolean immediate) {
  2600. addVariableToQueue(paintableId, variableName, map, immediate);
  2601. }
  2602. /**
  2603. * Sends a new value for the given paintables given variable to the server.
  2604. *
  2605. * The update is actually queued to be sent at a suitable time. If immediate
  2606. * is true, the update is sent as soon as possible. If immediate is false,
  2607. * the update will be sent along with the next immediate update.
  2608. *
  2609. * A null array is sent as an empty array.
  2610. *
  2611. * @param paintableId
  2612. * the id of the paintable that owns the variable
  2613. * @param variableName
  2614. * the name of the variable
  2615. * @param values
  2616. * the new value to be sent
  2617. * @param immediate
  2618. * true if the update is to be sent as soon as possible
  2619. */
  2620. public void updateVariable(String paintableId, String variableName,
  2621. String[] values, boolean immediate) {
  2622. addVariableToQueue(paintableId, variableName, values, immediate);
  2623. }
  2624. /**
  2625. * Sends a new value for the given paintables given variable to the server.
  2626. *
  2627. * The update is actually queued to be sent at a suitable time. If immediate
  2628. * is true, the update is sent as soon as possible. If immediate is false,
  2629. * the update will be sent along with the next immediate update. </p>
  2630. *
  2631. * A null array is sent as an empty array.
  2632. *
  2633. *
  2634. * @param paintableId
  2635. * the id of the paintable that owns the variable
  2636. * @param variableName
  2637. * the name of the variable
  2638. * @param values
  2639. * the new value to be sent
  2640. * @param immediate
  2641. * true if the update is to be sent as soon as possible
  2642. */
  2643. public void updateVariable(String paintableId, String variableName,
  2644. Object[] values, boolean immediate) {
  2645. addVariableToQueue(paintableId, variableName, values, immediate);
  2646. }
  2647. /**
  2648. * Does absolutely nothing. Replaced by {@link LayoutManager}.
  2649. *
  2650. * @param container
  2651. * @deprecated As of 7.0, serves no purpose
  2652. */
  2653. @Deprecated
  2654. public void runDescendentsLayout(HasWidgets container) {
  2655. }
  2656. /**
  2657. * This will cause re-layouting of all components. Mainly used for
  2658. * development. Published to JavaScript.
  2659. */
  2660. public void forceLayout() {
  2661. Duration duration = new Duration();
  2662. layoutManager.forceLayout();
  2663. VConsole.log("forceLayout in " + duration.elapsedMillis() + " ms");
  2664. }
  2665. /**
  2666. * Returns false
  2667. *
  2668. * @param paintable
  2669. * @return false, always
  2670. * @deprecated As of 7.0, serves no purpose
  2671. */
  2672. @Deprecated
  2673. private boolean handleComponentRelativeSize(ComponentConnector paintable) {
  2674. return false;
  2675. }
  2676. /**
  2677. * Returns false
  2678. *
  2679. * @param paintable
  2680. * @return false, always
  2681. * @deprecated As of 7.0, serves no purpose
  2682. */
  2683. @Deprecated
  2684. public boolean handleComponentRelativeSize(Widget widget) {
  2685. return handleComponentRelativeSize(connectorMap.getConnector(widget));
  2686. }
  2687. @Deprecated
  2688. public ComponentConnector getPaintable(UIDL uidl) {
  2689. // Non-component connectors shouldn't be painted from legacy connectors
  2690. return (ComponentConnector) getConnector(uidl.getId(),
  2691. Integer.parseInt(uidl.getTag()));
  2692. }
  2693. /**
  2694. * Get either an existing ComponentConnector or create a new
  2695. * ComponentConnector with the given type and id.
  2696. *
  2697. * If a ComponentConnector with the given id already exists, returns it.
  2698. * Otherwise creates and registers a new ComponentConnector of the given
  2699. * type.
  2700. *
  2701. * @param connectorId
  2702. * Id of the paintable
  2703. * @param connectorType
  2704. * Type of the connector, as passed from the server side
  2705. *
  2706. * @return Either an existing ComponentConnector or a new ComponentConnector
  2707. * of the given type
  2708. */
  2709. public ServerConnector getConnector(String connectorId, int connectorType) {
  2710. if (!connectorMap.hasConnector(connectorId)) {
  2711. return createAndRegisterConnector(connectorId, connectorType);
  2712. }
  2713. return connectorMap.getConnector(connectorId);
  2714. }
  2715. /**
  2716. * Creates a new ServerConnector with the given type and id.
  2717. *
  2718. * Creates and registers a new ServerConnector of the given type. Should
  2719. * never be called with the connector id of an existing connector.
  2720. *
  2721. * @param connectorId
  2722. * Id of the new connector
  2723. * @param connectorType
  2724. * Type of the connector, as passed from the server side
  2725. *
  2726. * @return A new ServerConnector of the given type
  2727. */
  2728. private ServerConnector createAndRegisterConnector(String connectorId,
  2729. int connectorType) {
  2730. Profiler.enter("ApplicationConnection.createAndRegisterConnector");
  2731. // Create and register a new connector with the given type
  2732. ServerConnector p = widgetSet.createConnector(connectorType,
  2733. configuration);
  2734. connectorMap.registerConnector(connectorId, p);
  2735. p.doInit(connectorId, this);
  2736. Profiler.leave("ApplicationConnection.createAndRegisterConnector");
  2737. return p;
  2738. }
  2739. /**
  2740. * Gets a recource that has been pre-loaded via UIDL, such as custom
  2741. * layouts.
  2742. *
  2743. * @param name
  2744. * identifier of the resource to get
  2745. * @return the resource
  2746. */
  2747. public String getResource(String name) {
  2748. return resourcesMap.get(name);
  2749. }
  2750. /**
  2751. * Singleton method to get instance of app's context menu.
  2752. *
  2753. * @return VContextMenu object
  2754. */
  2755. public VContextMenu getContextMenu() {
  2756. if (contextMenu == null) {
  2757. contextMenu = new VContextMenu();
  2758. contextMenu.setOwner(uIConnector.getWidget());
  2759. DOM.setElementProperty(contextMenu.getElement(), "id",
  2760. "PID_VAADIN_CM");
  2761. }
  2762. return contextMenu;
  2763. }
  2764. /**
  2765. * Gets an {@link Icon} instance corresponding to a URI.
  2766. *
  2767. * @since 7.2
  2768. * @param uri
  2769. * @return Icon object
  2770. */
  2771. public Icon getIcon(String uri) {
  2772. Icon icon;
  2773. if (uri == null) {
  2774. return null;
  2775. } else if (FontIcon.isFontIconUri(uri)) {
  2776. icon = GWT.create(FontIcon.class);
  2777. } else {
  2778. icon = GWT.create(ImageIcon.class);
  2779. }
  2780. icon.setUri(translateVaadinUri(uri));
  2781. return icon;
  2782. }
  2783. /**
  2784. * Translates custom protocols in UIDL URI's to be recognizable by browser.
  2785. * All uri's from UIDL should be routed via this method before giving them
  2786. * to browser due URI's in UIDL may contain custom protocols like theme://.
  2787. *
  2788. * @param uidlUri
  2789. * Vaadin URI from uidl
  2790. * @return translated URI ready for browser
  2791. */
  2792. public String translateVaadinUri(String uidlUri) {
  2793. if (uidlUri == null) {
  2794. return null;
  2795. }
  2796. if (uidlUri.startsWith("theme://")) {
  2797. final String themeUri = getThemeUri();
  2798. if (themeUri == null) {
  2799. VConsole.error("Theme not set: ThemeResource will not be found. ("
  2800. + uidlUri + ")");
  2801. }
  2802. uidlUri = themeUri + uidlUri.substring(7);
  2803. }
  2804. if (uidlUri.startsWith(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX)) {
  2805. // getAppUri *should* always end with /
  2806. // substring *should* always start with / (published:///foo.bar
  2807. // without published://)
  2808. uidlUri = ApplicationConstants.APP_PROTOCOL_PREFIX
  2809. + ApplicationConstants.PUBLISHED_FILE_PATH
  2810. + uidlUri
  2811. .substring(ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX
  2812. .length());
  2813. // Let translation of app:// urls take care of the rest
  2814. }
  2815. if (uidlUri.startsWith(ApplicationConstants.APP_PROTOCOL_PREFIX)) {
  2816. String relativeUrl = uidlUri
  2817. .substring(ApplicationConstants.APP_PROTOCOL_PREFIX
  2818. .length());
  2819. ApplicationConfiguration conf = getConfiguration();
  2820. String serviceUrl = conf.getServiceUrl();
  2821. if (conf.useServiceUrlPathParam()) {
  2822. // Should put path in v-resourcePath parameter and append query
  2823. // params to base portlet url
  2824. String[] parts = relativeUrl.split("\\?", 2);
  2825. String path = parts[0];
  2826. // If there's a "?" followed by something, append it as a query
  2827. // string to the base URL
  2828. if (parts.length > 1) {
  2829. String appUrlParams = parts[1];
  2830. serviceUrl = addGetParameters(serviceUrl, appUrlParams);
  2831. }
  2832. if (!path.startsWith("/")) {
  2833. path = '/' + path;
  2834. }
  2835. String pathParam = conf.getServiceUrlParameterName() + "="
  2836. + URL.encodeQueryString(path);
  2837. serviceUrl = addGetParameters(serviceUrl, pathParam);
  2838. uidlUri = serviceUrl;
  2839. } else {
  2840. uidlUri = serviceUrl + relativeUrl;
  2841. }
  2842. }
  2843. if (uidlUri.startsWith(ApplicationConstants.VAADIN_PROTOCOL_PREFIX)) {
  2844. final String vaadinUri = configuration.getVaadinDirUrl();
  2845. String relativeUrl = uidlUri
  2846. .substring(ApplicationConstants.VAADIN_PROTOCOL_PREFIX
  2847. .length());
  2848. uidlUri = vaadinUri + relativeUrl;
  2849. }
  2850. return uidlUri;
  2851. }
  2852. /**
  2853. * Gets the URI for the current theme. Can be used to reference theme
  2854. * resources.
  2855. *
  2856. * @return URI to the current theme
  2857. */
  2858. public String getThemeUri() {
  2859. return configuration.getVaadinDirUrl() + "themes/"
  2860. + getUIConnector().getActiveTheme();
  2861. }
  2862. /**
  2863. * Listens for Notification hide event, and redirects. Used for system
  2864. * messages, such as session expired.
  2865. *
  2866. */
  2867. private class NotificationRedirect implements VNotification.EventListener {
  2868. String url;
  2869. NotificationRedirect(String url) {
  2870. this.url = url;
  2871. }
  2872. @Override
  2873. public void notificationHidden(HideEvent event) {
  2874. redirect(url);
  2875. }
  2876. }
  2877. /* Extended title handling */
  2878. private final VTooltip tooltip;
  2879. private ConnectorMap connectorMap = GWT.create(ConnectorMap.class);
  2880. protected String getUidlSecurityKey() {
  2881. return getCsrfToken();
  2882. }
  2883. /**
  2884. * Gets the token (aka double submit cookie) that the server uses to protect
  2885. * against Cross Site Request Forgery attacks.
  2886. *
  2887. * @return the CSRF token string
  2888. */
  2889. public String getCsrfToken() {
  2890. return csrfToken;
  2891. }
  2892. /**
  2893. * Use to notify that the given component's caption has changed; layouts may
  2894. * have to be recalculated.
  2895. *
  2896. * @param component
  2897. * the Paintable whose caption has changed
  2898. * @deprecated As of 7.0.2, has not had any effect for a long time
  2899. */
  2900. @Deprecated
  2901. public void captionSizeUpdated(Widget widget) {
  2902. // This doesn't do anything, it's just kept here for compatibility
  2903. }
  2904. /**
  2905. * Gets the main view
  2906. *
  2907. * @return the main view
  2908. */
  2909. public UIConnector getUIConnector() {
  2910. return uIConnector;
  2911. }
  2912. /**
  2913. * Gets the {@link ApplicationConfiguration} for the current application.
  2914. *
  2915. * @see ApplicationConfiguration
  2916. * @return the configuration for this application
  2917. */
  2918. public ApplicationConfiguration getConfiguration() {
  2919. return configuration;
  2920. }
  2921. /**
  2922. * Checks if there is a registered server side listener for the event. The
  2923. * list of events which has server side listeners is updated automatically
  2924. * before the component is updated so the value is correct if called from
  2925. * updatedFromUIDL.
  2926. *
  2927. * @param paintable
  2928. * The connector to register event listeners for
  2929. * @param eventIdentifier
  2930. * The identifier for the event
  2931. * @return true if at least one listener has been registered on server side
  2932. * for the event identified by eventIdentifier.
  2933. * @deprecated As of 7.0. Use
  2934. * {@link AbstractComponentState#hasEventListener(String)}
  2935. * instead
  2936. */
  2937. @Deprecated
  2938. public boolean hasEventListeners(ComponentConnector paintable,
  2939. String eventIdentifier) {
  2940. return paintable.hasEventListener(eventIdentifier);
  2941. }
  2942. /**
  2943. * Adds the get parameters to the uri and returns the new uri that contains
  2944. * the parameters.
  2945. *
  2946. * @param uri
  2947. * The uri to which the parameters should be added.
  2948. * @param extraParams
  2949. * One or more parameters in the format "a=b" or "c=d&e=f". An
  2950. * empty string is allowed but will not modify the url.
  2951. * @return The modified URI with the get parameters in extraParams added.
  2952. */
  2953. public static String addGetParameters(String uri, String extraParams) {
  2954. if (extraParams == null || extraParams.length() == 0) {
  2955. return uri;
  2956. }
  2957. // RFC 3986: The query component is indicated by the first question
  2958. // mark ("?") character and terminated by a number sign ("#") character
  2959. // or by the end of the URI.
  2960. String fragment = null;
  2961. int hashPosition = uri.indexOf('#');
  2962. if (hashPosition != -1) {
  2963. // Fragment including "#"
  2964. fragment = uri.substring(hashPosition);
  2965. // The full uri before the fragment
  2966. uri = uri.substring(0, hashPosition);
  2967. }
  2968. if (uri.contains("?")) {
  2969. uri += "&";
  2970. } else {
  2971. uri += "?";
  2972. }
  2973. uri += extraParams;
  2974. if (fragment != null) {
  2975. uri += fragment;
  2976. }
  2977. return uri;
  2978. }
  2979. ConnectorMap getConnectorMap() {
  2980. return connectorMap;
  2981. }
  2982. /**
  2983. * @deprecated As of 7.0. No longer serves any purpose.
  2984. */
  2985. @Deprecated
  2986. public void unregisterPaintable(ServerConnector p) {
  2987. VConsole.log("unregisterPaintable (unnecessarily) called for "
  2988. + Util.getConnectorString(p));
  2989. }
  2990. /**
  2991. * Get VTooltip instance related to application connection
  2992. *
  2993. * @return VTooltip instance
  2994. */
  2995. public VTooltip getVTooltip() {
  2996. return tooltip;
  2997. }
  2998. /**
  2999. * Method provided for backwards compatibility. Duties previously done by
  3000. * this method is now handled by the state change event handler in
  3001. * AbstractComponentConnector. The only function this method has is to
  3002. * return true if the UIDL is a "cached" update.
  3003. *
  3004. * @param component
  3005. * @param uidl
  3006. * @param manageCaption
  3007. * @deprecated As of 7.0, no longer serves any purpose
  3008. * @return
  3009. */
  3010. @Deprecated
  3011. public boolean updateComponent(Widget component, UIDL uidl,
  3012. boolean manageCaption) {
  3013. ComponentConnector connector = getConnectorMap()
  3014. .getConnector(component);
  3015. if (!AbstractComponentConnector.isRealUpdate(uidl)) {
  3016. return true;
  3017. }
  3018. if (!manageCaption) {
  3019. VConsole.error(Util.getConnectorString(connector)
  3020. + " called updateComponent with manageCaption=false. The parameter was ignored - override delegateCaption() to return false instead. It is however not recommended to use caption this way at all.");
  3021. }
  3022. return false;
  3023. }
  3024. /**
  3025. * @deprecated As of 7.0. Use
  3026. * {@link AbstractComponentConnector#hasEventListener(String)}
  3027. * instead
  3028. */
  3029. @Deprecated
  3030. public boolean hasEventListeners(Widget widget, String eventIdentifier) {
  3031. ComponentConnector connector = getConnectorMap().getConnector(widget);
  3032. if (connector == null) {
  3033. /*
  3034. * No connector will exist in cases where Vaadin widgets have been
  3035. * re-used without implementing server<->client communication.
  3036. */
  3037. return false;
  3038. }
  3039. return hasEventListeners(getConnectorMap().getConnector(widget),
  3040. eventIdentifier);
  3041. }
  3042. LayoutManager getLayoutManager() {
  3043. return layoutManager;
  3044. }
  3045. /**
  3046. * Schedules a heartbeat request to occur after the configured heartbeat
  3047. * interval elapses if the interval is a positive number. Otherwise, does
  3048. * nothing.
  3049. *
  3050. * @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead
  3051. */
  3052. @Deprecated
  3053. protected void scheduleHeartbeat() {
  3054. heartbeat.schedule();
  3055. }
  3056. /**
  3057. * Sends a heartbeat request to the server.
  3058. * <p>
  3059. * Heartbeat requests are used to inform the server that the client-side is
  3060. * still alive. If the client page is closed or the connection lost, the
  3061. * server will eventually close the inactive UI.
  3062. *
  3063. * @deprecated as of 7.2, use {@link Heartbeat#send()} instead
  3064. */
  3065. @Deprecated
  3066. protected void sendHeartbeat() {
  3067. heartbeat.send();
  3068. }
  3069. /**
  3070. * Timer used to make sure that no misbehaving components can delay response
  3071. * handling forever.
  3072. */
  3073. Timer forceHandleMessage = new Timer() {
  3074. @Override
  3075. public void run() {
  3076. VConsole.log("WARNING: reponse handling was never resumed, forcibly removing locks...");
  3077. responseHandlingLocks.clear();
  3078. handlePendingMessages();
  3079. }
  3080. };
  3081. /**
  3082. * This method can be used to postpone rendering of a response for a short
  3083. * period of time (e.g. to avoid the rendering process during animation).
  3084. *
  3085. * @param lock
  3086. */
  3087. public void suspendReponseHandling(Object lock) {
  3088. responseHandlingLocks.add(lock);
  3089. }
  3090. /**
  3091. * Resumes the rendering process once all locks have been removed.
  3092. *
  3093. * @param lock
  3094. */
  3095. public void resumeResponseHandling(Object lock) {
  3096. responseHandlingLocks.remove(lock);
  3097. if (responseHandlingLocks.isEmpty()) {
  3098. // Cancel timer that breaks the lock
  3099. forceHandleMessage.cancel();
  3100. if (!pendingUIDLMessages.isEmpty()) {
  3101. VConsole.log("No more response handling locks, handling pending requests.");
  3102. handlePendingMessages();
  3103. }
  3104. }
  3105. }
  3106. /**
  3107. * Handles all pending UIDL messages queued while response handling was
  3108. * suspended.
  3109. */
  3110. private void handlePendingMessages() {
  3111. if (!pendingUIDLMessages.isEmpty()) {
  3112. /*
  3113. * Clear the list before processing enqueued messages to support
  3114. * reentrancy
  3115. */
  3116. List<PendingUIDLMessage> pendingMessages = pendingUIDLMessages;
  3117. pendingUIDLMessages = new ArrayList<PendingUIDLMessage>();
  3118. for (PendingUIDLMessage pending : pendingMessages) {
  3119. handleReceivedJSONMessage(pending.getStart(),
  3120. pending.getJsonText(), pending.getJson());
  3121. }
  3122. }
  3123. }
  3124. private boolean handleErrorInDelegate(String details, int statusCode) {
  3125. if (communicationErrorDelegate == null) {
  3126. return false;
  3127. }
  3128. return communicationErrorDelegate.onError(details, statusCode);
  3129. }
  3130. /**
  3131. * Sets the delegate that is called whenever a communication error occurrs.
  3132. *
  3133. * @param delegate
  3134. * the delegate.
  3135. */
  3136. public void setCommunicationErrorDelegate(CommunicationErrorHandler delegate) {
  3137. communicationErrorDelegate = delegate;
  3138. }
  3139. public void setApplicationRunning(boolean running) {
  3140. if (applicationRunning && !running) {
  3141. eventBus.fireEvent(new ApplicationStoppedEvent());
  3142. }
  3143. applicationRunning = running;
  3144. }
  3145. public boolean isApplicationRunning() {
  3146. return applicationRunning;
  3147. }
  3148. public <H extends EventHandler> HandlerRegistration addHandler(
  3149. GwtEvent.Type<H> type, H handler) {
  3150. return eventBus.addHandler(type, handler);
  3151. }
  3152. @Override
  3153. public void fireEvent(GwtEvent<?> event) {
  3154. eventBus.fireEvent(event);
  3155. }
  3156. /**
  3157. * Calls {@link ComponentConnector#flush()} on the active connector. Does
  3158. * nothing if there is no active (focused) connector.
  3159. */
  3160. public void flushActiveConnector() {
  3161. ComponentConnector activeConnector = getActiveConnector();
  3162. if (activeConnector == null) {
  3163. return;
  3164. }
  3165. activeConnector.flush();
  3166. }
  3167. /**
  3168. * Gets the active connector for focused element in browser.
  3169. *
  3170. * @return Connector for focused element or null.
  3171. */
  3172. private ComponentConnector getActiveConnector() {
  3173. Element focusedElement = Util.getFocusedElement();
  3174. if (focusedElement == null) {
  3175. return null;
  3176. }
  3177. return Util.getConnectorForElement(this, getUIConnector().getWidget(),
  3178. focusedElement);
  3179. }
  3180. /**
  3181. * Sets the status for the push connection.
  3182. *
  3183. * @param enabled
  3184. * <code>true</code> to enable the push connection;
  3185. * <code>false</code> to disable the push connection.
  3186. */
  3187. public void setPushEnabled(boolean enabled) {
  3188. final PushConfigurationState pushState = uIConnector.getState().pushConfiguration;
  3189. if (enabled && push == null) {
  3190. push = GWT.create(PushConnection.class);
  3191. push.init(this, pushState, new CommunicationErrorHandler() {
  3192. @Override
  3193. public boolean onError(String details, int statusCode) {
  3194. showCommunicationError(details, statusCode);
  3195. return true;
  3196. }
  3197. });
  3198. } else if (!enabled && push != null && push.isActive()) {
  3199. push.disconnect(new Command() {
  3200. @Override
  3201. public void execute() {
  3202. push = null;
  3203. /*
  3204. * If push has been enabled again while we were waiting for
  3205. * the old connection to disconnect, now is the right time
  3206. * to open a new connection
  3207. */
  3208. if (pushState.mode.isEnabled()) {
  3209. setPushEnabled(true);
  3210. }
  3211. /*
  3212. * Send anything that was enqueued while we waited for the
  3213. * connection to close
  3214. */
  3215. if (pendingInvocations.size() > 0) {
  3216. sendPendingVariableChanges();
  3217. }
  3218. }
  3219. });
  3220. }
  3221. }
  3222. public void handlePushMessage(String message) {
  3223. handleJSONText(message, 200);
  3224. }
  3225. /**
  3226. * Returns a human readable string representation of the method used to
  3227. * communicate with the server.
  3228. *
  3229. * @since 7.1
  3230. * @return A string representation of the current transport type
  3231. */
  3232. public String getCommunicationMethodName() {
  3233. if (push != null) {
  3234. return "Push (" + push.getTransportType() + ")";
  3235. } else {
  3236. return "XHR";
  3237. }
  3238. }
  3239. private static Logger getLogger() {
  3240. return Logger.getLogger(ApplicationConnection.class.getName());
  3241. }
  3242. /**
  3243. * Returns the hearbeat instance.
  3244. */
  3245. public Heartbeat getHeartbeat() {
  3246. return heartbeat;
  3247. }
  3248. }