Weitere Ă€hnliche Inhalte Ăhnlich wie RIAs Done Right: Grails, Flex, and EXT GWT (20) Mehr von Michael Galpin (12) KĂŒrzlich hochgeladen (20) RIAs Done Right: Grails, Flex, and EXT GWT16. Welcome to 2009
Request Web Ser ver
App
Request
Browser Data
App Server
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
26. $$$$$$$$$$$$$$$
$$$$$$$$
Less (Ser ver)Processing
Less Ser vers
More Money
27. $$$$$$$$$$$$$$$
$$$$$$$$
Less (Ser ver)Processing More (Client) Processing
Less Ser vers More Cores
More Money Free! (Thanks Users)
28. $$$$$$$$$$$$$$$
$$$$$$$$
Less (Ser ver)Processing More (Client) Processing
Less Ser vers More Cores
More Money Free! (Thanks Users)
Less Bandwidth
More Money
37. Ajax?
Request
Browser
38. Ajax?
Request
Browser App Server
39. Ajax?
Request
Browser App Server
40. Ajax?
Request
HTML+JS
Browser App Server
41. Ajax?
Request
HTML+JS
Browser App Server
42. Ajax?
Request
HTML+JS
XHR
Browser App Server
43. Ajax?
Request
HTML+JS
XHR
Browser ??? App Server
44. Ajax?
Request
HTML+JS
XHR
Browser HTML?
App Server
45. Ajax?
Request
HTML+JS
XHR
Browser Data?
App Server
46. Ajax?
Request
HTML+JS
XHR
Browser Data?
App Server
51. Step 1. Get Serious
About SOA
X
âąREpresentational
âąState
âąTransfer
âą?
52. Step 2. Get Tools
JAX-RS (JSR-311)
s ey
Jer
J
Apa
che
RESTEasy CXF
55. Domain Model
class Story {
String link
String title
String description
String tags
String category
int votesFor
int votesAgainst
}
56. 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;)
}
}
57. 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){
// ....
}
}
58. 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
}
}
62. 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;
}
}
63. 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);
}
});
}
}
64. 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;
}
}
65. 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);
}
}
67. 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;
}
}
71. 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>
72. 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;/>
73. 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);
}
75. Now Is The Time
Questions
Protests
Cries for Help
Confessions
Donations