RIAs Done Right: Grails, Flex, and EXT GWT

  • 4,087 views
Uploaded on

Your users want a more advanced user interface. You know that your system needs a service-oriented architecture. You're in luck! These two things actually go hand in hand. Not only can you get the …

Your users want a more advanced user interface. You know that your system needs a service-oriented architecture. You're in luck! These two things actually go hand in hand. Not only can you get the best of both worlds but modern technologies and tools even make it fun to develop these systems. Find out how to build RESTful back-end systems with Grails. You can easily add a Flex front end, or you can try Ext GWT: a combination of Ext JS's rich widgets -- all on the Java™ platform, thanks to the Google Web Toolkit.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,087
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
266
Comments
0
Likes
7

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide











































































Transcript

  • 1. RIAs Done Right Michael Galpin, eBay @michaelg
  • 2. Traditional Web Applications
  • 3. Traditional Web Applications Browser
  • 4. Traditional Web Applications Request Browser
  • 5. Traditional Web Applications Request Ser ver Browser
  • 6. Traditional Web Applications Request HTML Ser ver Browser
  • 7. Traditional Web Applications Request HTML Ser ver Browser
  • 8. Welcome to 2009
  • 9. Welcome to 2009 Browser
  • 10. Welcome to 2009 Request Browser
  • 11. Welcome to 2009 Request Web Ser ver Browser
  • 12. Welcome to 2009 Request Web Ser ver App Browser
  • 13. Welcome to 2009 Request Web Ser ver App Browser
  • 14. Welcome to 2009 Request Web Ser ver App Browser
  • 15. Welcome to 2009 Request Web Ser ver App Request Browser
  • 16. Welcome to 2009 Request Web Ser ver App Request Browser Data App Server
  • 17. Why?
  • 18. Architecture
  • 19. Architecture client server 1998 pres app “Hello World”
  • 20. Architecture client server 1998 pres app “Hello World” client server 2003 pres app Ajax
  • 21. Architecture client server 1998 pres app “Hello World” client server 2003 pres app Ajax client ser ver 2009 pres app RIA
  • 22. Performance (seriously)
  • 23. $$$$$$$$$$$$$$$ $$$$$$$$
  • 24. $$$$$$$$$$$$$$$ $$$$$$$$ Less (Ser ver)Processing Less Ser vers More Money
  • 25. $$$$$$$$$$$$$$$ $$$$$$$$ Less (Ser ver)Processing More (Client) Processing Less Ser vers More Cores More Money Free! (Thanks Users)
  • 26. $$$$$$$$$$$$$$$ $$$$$$$$ Less (Ser ver)Processing More (Client) Processing Less Ser vers More Cores More Money Free! (Thanks Users) Less Bandwidth More Money
  • 27. Mobility
  • 28. Mobility a.app a.com
  • 29. Mobility a.app a.app a.com b.com
  • 30. Mobility a.app a.app a.app a.com b.com facebook.com
  • 31. Mobility a.app a.app a.app a.com b.com facebook.com a.app a.air
  • 32. But What About Ajax?
  • 33. Ajax?
  • 34. Ajax? Browser
  • 35. Ajax? Request Browser
  • 36. Ajax? Request Browser App Server
  • 37. Ajax? Request Browser App Server
  • 38. Ajax? Request HTML+JS Browser App Server
  • 39. Ajax? Request HTML+JS Browser App Server
  • 40. Ajax? Request HTML+JS XHR Browser App Server
  • 41. Ajax? Request HTML+JS XHR Browser ??? App Server
  • 42. Ajax? Request HTML+JS XHR Browser HTML? App Server
  • 43. Ajax? Request HTML+JS XHR Browser Data? App Server
  • 44. Ajax? Request HTML+JS XHR Browser Data? App Server
  • 45. How?
  • 46. Step 1. Get Serious About SOA
  • 47. Step 1. Get Serious About SOA
  • 48. Step 1. Get Serious About SOA X
  • 49. Step 1. Get Serious About SOA X •REpresentational •State •Transfer •?
  • 50. Step 2. Get Tools JAX-RS (JSR-311) s ey Jer J Apa che RESTEasy CXF
  • 51. Domain Model class Story { String link String title String description String tags String category int votesFor int votesAgainst }
  • 52. Services class SearchService { boolean transactional = false def list() { Story.list() } def listCategory(catName){ Story.findAllWhere(category:catName) } def searchTag(tag){ Story.findAllByTagsIlike(quot;%quot;+tag+quot;%quot;) } }
  • 53. class StoryService { Services boolean transactional = true def create(story) { story.votesFor = 0 story.votesAgainst = 0 if(!story.save(flush:true) ) { story.errors.each { log.error(it) } } story } def voteFor(storyId){ def story = Story.get(storyId) story.votesFor += 1 if(!story.save(flush:true) ) { story.errors.each { log.error(it) } } story } def voteAgainst(storyId){ // .... } }
  • 54. class ApiController { Controllers // injected services def searchService def storyService def search = { def results= null def tagResults = null if (params.tag){ tagResults = searchService.searchTag(params.tag) } def catResults = null if (params.category){ catResults = searchService.listCategory(params.category) } if (params.tag && params.category){ def tagMap = [:] tagResults.each{ story -> tagMap[story.id] = story } results = catResults.findAll { tagMap[it.id] != null} } else { if (params.category){ results = catResults } else { results = tagResults } } render results as JSON } def digg = { def story = storyService.voteFor(params.id) render story as XML } }
  • 55. Step 3
  • 56. Step 3a. Just Pick One
  • 57. GWT in Action public class DiggApp implements EntryPoint { private HorizontalPanel createSearchForm() { HorizontalPanel panel = new HorizontalPanel(); panel.setTitle(quot;Search for Storiesquot;); Label tagLabel = new Label(quot;Tag:quot;); final TextBox tagBox = new TextBox(); panel.add(tagLabel); panel.add(tagBox); Label catLabel = new Label(quot;Category:quot;); final ListBox catBox = new ListBox(); catBox.addItem(quot;quot;); for (String[] category : CATEGORIES){ catBox.addItem(category[0],category[1]); } panel.add(catLabel); panel.add(catBox); Button searchBtn = new Button(quot;Searchquot;); searchBtn.addClickListener(new ClickListener(){ public void onClick(Widget sender) { search(tagBox.getText(), catBox.getValue(catBox.getSelectedIndex())); } }); panel.add(searchBtn); return panel; } }
  • 58. Mo’ GWT public class DiggApp implements EntryPoint { private final void search(String tag, String category){ resultsPanel.clear(); Story.search(tag, category, new RequestCallback(){ public void onError(Request request, Throwable exception) { Label label = new Label(quot;Sorry there was an errorquot;); resultsPanel.add(label); } public void onResponseReceived(Request request, Response response) { List<Story> stories = Story.fromJson(response.getText()); //SearchTable grid = new SearchTable(stories); StoryGrid grid = new StoryGrid(stories); resultsPanel.add(grid); } }); } }
  • 59. public class Story { GWT Models public Story(JSONObject obj){ id = (int) obj.get(quot;idquot;).isNumber().doubleValue(); link = obj.get(quot;linkquot;).isString().stringValue(); title = obj.get(quot;titlequot;).isString().stringValue(); description = obj.get(quot;descriptionquot;).isString().stringValue(); tags = obj.get(quot;tagsquot;).isString().stringValue(); category = obj.get(quot;categoryquot;).isString().stringValue(); votesFor = (int) obj.get(quot;votesForquot;).isNumber().doubleValue(); votesAgainst = (int) obj.get(quot;votesAgainstquot;).isNumber().doubleValue(); } public static void search(String tag, String category, RequestCallback callback){ String url = buildRequest(tag, category); RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url); try { builder.sendRequest(null, callback); } catch (RequestException e) { callback.onError(null, e); } } public static List<Story> fromJson(String jsonText){ JSONValue val = JSONParser.parse(jsonText); JSONArray array = val.isArray(); List<Story> stories = new ArrayList<Story>(array.size()); for (int i=0;i<array.size();i++){ Story story = new Story(array.get(i).isObject()); stories.add(story); } return stories; } }
  • 60. Plain ‘Ol GWT Table public class SearchTable extends FlexTable { private List<Story> stories; public SearchTable(List<Story> stories) { super(); this.stories = stories; this.buildTable(); } private void buildTable(){ this.setBorderWidth(2); this.setText(0, 0, quot;storyquot;); this.setText(0, 1, quot;categoryquot;); this.setText(0, 2, quot;descriptionquot;); if (stories.size() == 0){ showMessage(quot;Sorry there were no resultsquot;); } else { for (int i=0;i<stories.size();i++){ Story story = stories.get(i); setWidget(i+1, 0, story.getTitleLink()); setText(i+1, 1, story.getCategory()); setText(i+1, 2, story.getDescription()); } } } private void showMessage(String msg){ setText(1,0, msg); getFlexCellFormatter().setColSpan(1, 0, 3); } }
  • 61. Pimp my GWT public class StoryGrid extends LayoutContainer { public StoryGrid(List<Story> stories){ this.setLayout(new FlowLayout(10)); this.setSize(750, 300); ListStore<BaseModelData> store = this.buildDataModel(stories); Grid<BaseModelData> grid = new Grid<BaseModelData>(store, createColumnModel()); grid.setBorders(true); add(grid); } private ColumnModel createColumnModel(){ List<ColumnConfig> configs = new ArrayList<ColumnConfig>(); ColumnConfig column = new ColumnConfig(); column.setId(quot;titleLinkquot;); column.setHeader(quot;Storyquot;); column.setWidth(200); configs.add(column); //... return new ColumnModel(configs); } private ListStore<BaseModelData> buildDataModel(List<Story> stories){ ListStore<BaseModelData> data = new ListStore<BaseModelData>(); for (Story story : stories){ BaseModelData model = new BaseModelData(story.properties()); data.add(model); } return data; } }
  • 62. I Want Your Flex
  • 63. MmmmXML <ctrl:DiggController xmlns:mx=quot;http://www.adobe.com/2006/mxmlquot; layout=quot;verticalquot; xmlns:works=quot;components.*quot; xmlns:ctrl=quot;controllers.*quot;> <mx:Script> <![CDATA[ import org.developerworks.digg.Story; private function digg():void { this.diggStory(results.selectedItem as Story); } private function bury():void { this.buryStory(results.selectedItem as Story); } ]]> </mx:Script> <ctrl:states> <mx:State name=quot;SubmitStoryquot;> <mx:AddChild relativeTo=quot;{buttons}quot; position=quot;afterquot;> <works:StoryEditor successHandler=quot;{this.submissionHandler}quot;/> </mx:AddChild> </mx:State> </ctrl:states> <mx:DataGrid id=quot;resultsquot; dataProvider=quot;{stories}quot; doubleClickEnabled=quot;truequot; doubleClick=quot;openStory(results.selectedItem as Story)quot;/> <mx:HBox id=quot;buttonsquot;> <mx:Button label=quot;Digg the Story!quot; click=quot;digg()quot;/> <mx:Button label=quot;Bury the Story!quot; click=quot;bury()quot;/> <mx:Button label=quot;{this.subBtnLabel}quot; click=quot;toggleSubmitStory()quot;/> </mx:HBox> </ctrl:DiggController>
  • 64. Components <mx:VBox xmlns:mx=quot;http://www.adobe.com/2006/mxmlquot; width=quot;100%quot; height=quot;100%quot;> <mx:Script> <![CDATA[ [Bindable] private var story:Story = new Story(); public var successHandler:Function; private function submitStory():void { story.addEventListener(DiggEvent.ON_STORY_SUBMIT_SUCCESS, successHandler); story.addEventListener(DiggEvent.ON_STORY_SUBMIT_FAILURE, errorHandler); story.save(); // reset story = new Story(); } ]]> </mx:Script> <mx:Form> <mx:FormHeading label=quot;Submit a New Storyquot;/> <mx:FormItem label=quot;What's the URL?quot; required=quot;truequot;> <mx:TextInput id=quot;linkBoxquot; toolTip=quot;Keep it Short and Sweetquot; text=quot;{story.link}quot;/> </mx:FormItem> <mx:FormItem label=quot;Give it a Titlequot; required=quot;truequot;> <mx:TextInput id=quot;titleBoxquot; text=quot;{story.title}quot;/> </mx:FormItem> <mx:FormItem label=quot;Pick a Categoryquot; required=quot;truequot;> <mx:ComboBox dataProvider=quot;{Story.CATEGORIES}quot; id=quot;categoryBoxquot; selectedIndex=quot;0quot;/> </mx:FormItem> <mx:FormItem label=quot;Give a Short Descriptionquot;> <mx:TextArea height=quot;60quot; width=quot;320quot; id=quot;descripBoxquot; text=quot;{story.description}quot;/> </mx:FormItem> <mx:FormItem label=quot;Tag Itquot;> <mx:TextInput id=quot;tagBoxquot; text=quot;{story.tags}quot;/> </mx:FormItem> <mx:Button label=quot;Submit It!quot; click=quot;submitStory()quot;/> </mx:Form> <mx:Binding source=quot;linkBox.textquot; destination=quot;story.linkquot;/> <mx:Binding source=quot;titleBox.textquot; destination=quot;story.titlequot;/> <mx:Binding source=quot;categoryBox.selectedItem.dataquot; destination=quot;story.categoryquot;/> <mx:Binding source=quot;descripBox.textquot; destination=quot;story.descriptionquot;/> <mx:Binding source=quot;tagBox.textquot; destination=quot;story.tagsquot;/>
  • 65. Flex Models public class Story extends EventDispatcher { public function Story(data:XML=null) { if (data) { id = data.@id; title = data.title; link = data.link; category = data.category; description = data.description; tags = data.tags; votesFor = Number(data.votesFor); votesAgainst = Number(data.votesAgainst); } } public function save():void { var req:URLRequest = new URLRequest(SUBMIT_URL); req.method = URLRequestMethod.POST; var params:URLVariables = new URLVariables(); params.title = title; params.link = link; params.category = category; params.description = description; params.tags = tags; req.data = params; submitStoryLoader = new URLLoader(req); submitStoryLoader.addEventListener(Event.COMPLETE, submitSuccessHandler); submitStoryLoader.addEventListener(IOErrorEvent.IO_ERROR, submitErrorHandler); submitStoryLoader.load(req); }
  • 66. Now Is The Time Questions Protests Cries for Help Confessions Donations