D2W Stateful Controllers
Fuego Digital Media QSTP-LLC
Daniel Roy
• Using WebObjects since 2004
• Work with government agencies, private companies and various
foundations globally
• Fuego Content Management System & Fuego Frameworks
• Presence in North America and Middle East
Fuego History
• 42% of community uses D2W
• “D2W is to WebObjects apps as the assembly line is to automobiles...you
provide customization directions (D2W rules) to the assembly line (D2W) to
build your end products (WOComponent pages).”
• Flow is controlled by delegates that manage the actions and direction of the
application
• Use ERDBranchDelegate to define the actions displayed on the page by rules
or via introspection
Direct To Web
The Basics
query select edit save
Start/Stop
Confirm
Edit
Confirm
Confirm
saveForLater
Save
Delete
edit
Edit CommentreadyToSubmit*
readyToSubmit*
saveAndSubmit
Submit
delete*
confirmDeletion
approveDeletion
confirm
edit*
List Expense
Claims
select
cancel
Notes:
- Cancel is present on all pages, but only
shown for first.
- Any branches with * are optional
and are shown based on business logic
- Any branches with the same name,
are the same page configuration, but
with different branches
Needs to enter
comment?
NO
Edit Comment
YES
WHAT?
WHAT???
??
Back in the day...
• Without Project Wonder, rely on nextPageDelegate and
nextPage for navigation
• nextPageDelegate is checked first, and if found call nextPage()
• without nextPageDelegate, nextPage() is called on the D2WPage
component directly
• Single class between each page
Next Page Delegate
D2WPage
NPD
(select)
D2WPage
NPD
(edit)
D2WPage
NPD
(query)
...
Plain WebObjects Controller Sample Code
public class ManageUserEditNextPageDelegate implements NextPageDelegate {
	 public WOComponent nextPage(WOComponent sender) {
	 	 NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects();
	 	 User currentUser = selectedObjects.get(0);
	 	 EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session());
	 	 epi.setNextPageDelegate(new ManageUserSaveNextPageDelegate());
	 	 epi.setObject(currentUser);
	 	 return (WOComponent) epi;
	 }
}
Enter Project Wonder
• ERDBranchDelegate to the rescue!
• Now you can show multiple branch choices for a page
• Pull choices from D2W context in the method
branchChoicesForContext(), using the D2W rule key
“branchChoices”
• Or, use introspection to find all methods that take a single
WOComponent as parameter
ERDBranchDelegate
D2WPage ERDBD
D2WPage
(search)
D2WPage
(add)
D2WPage
(cancel)
ERDBD
D2WPage
(save)
D2WPage
(cancel)
D2WPage
(add)
ERDBranchDelegate Sample Code
public class ManageUserControllerSelectBranchDelegate extends ERDBranchDelegate {
	 // Return an edit page for a single selected row in the pick page
	 public WOComponent edit(WOComponent sender) {
	 	 NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects();
	 	 User currentUser = selectedObjects.get(0);
	 	 EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session());
	 	 epi.setNextPageDelegate(new ManageUserControllerSaveBranchDelegate());
	 	 epi.setObject(currentUser);
	 	 return (WOComponent) epi;
	 }
	 // From the selected items in the pick page, perform a delete
	 public WOComponent delete(WOComponent sender) {
	 	 NSArray<User> users = ((ERDPickPageInterface) sender).selectedObjects();
	 	 for (User user : users) {
	 	 	 user.delete();
	 	 }
	 	 if (users.count() > 0) {
	 	 	 // Assuming all in same EC.
	 	 	 users.get(0).editingContext().saveChanges();
	 	 }
	 	 return sender.pageWithName("PageWrapper");
	 }
}
But wait...
• Must still set and get the
objects on each page
• Lots of classes to write
• Why not reuse the same
delegate?
Benefits of Delegate Reuse
• Enter the controller/delegate many times (one controller for
many pages)
• Reuse the stored state in editing context
• Support one or many flows within the single class
• Define the visible branches using rules
Re-entrant Branch Delegate
D2WPageBD
queryPage
search()
edit()
editPage
selectPage
save()
Is ERDBranchDelegate Enough?
• Yes....but...no.
• Branch choices are defined in the rules, but can lead to a
complex ruleset depending on the business logic
• Introspection on the ERDBranchDelegate returns all the
methods in the class
Hello (Stateful) World!
• FDStatefulController is a re-entrant branch delegate extending
ERDBranchDelegate
• Better separation of MVC
• Override branchChoicesForContext(D2WContext context)
• Reuse page configurations (define the PC in code)
• store state between pages
• editing context
• EOs, etc in subclasses
branchChoicesForContext
public class StatefulController extends ERDBranchDelegate {
	 private NSArray<?> branches;
	 private EOEditingContext editingContext;
	 public NSArray<?> getBranches() {
	 	 return branches;
	 }
	 public void setBranches(NSArray<?> branches) {
	 	 this.branches = branches;
	 }
	
	 public EOEditingContext editingContext() {
	 	 if (this.editingContext == null) {
	 	 	 this.editingContext = ERXEC.newEditingContext();
	 	 }
	 	 return this.editingContext;
	 }
	 public NSArray branchChoicesForContext(D2WContext context) {
	 	 NSArray choices = getBranches();
	 if (choices == null) {
	 	 	 branches = (NSArray) context.valueForKey(BRANCH_CHOICES);
} else {
	 NSMutableArray translatedChoices = new NSMutableArray();
	 for (Iterator iter = choices.iterator(); iter.hasNext();) {
... rest of method continues the same as ERDBranchDelegate
StatefulController Code
// Return an edit page for a single selected row in the pick page
public WOComponent edit(WOComponent sender) {
	 NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects();
	 User currentUser = selectedObjects.get(0);
	 setBranches(new NSArray("save"));
	 EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session());
	 epi.setNextPageDelegate(this);
	 epi.setObject(currentUser);
	 return (WOComponent) epi;
}
public WOComponent save(WOComponent sender) {
	 editingContext().saveChanges();
	 return sender.pageWithName("PageWrapper");
}
// From the selected items in the pick page, perform a delete
public WOComponent delete(WOComponent sender) {
	 setBranches(null);
	 NSArray<User> users = ((ERDPickPageInterface) sender).selectedObjects();
	 ERXUtilities.deleteObjects(editingContext(), users);
	 return save(sender);
}
Page Utility Methods
Interaction
editPage
queryPage
listPage
inspectPage
messagePage
pickPage
selectPage
Utility
editingContext()
nestedEditingContext
save()
Utility Method Examples
protected EditPageInterface editPage(Session session, String pageConfigurationName, EOEnterpriseObject enterpriseObject) {
	 EditPageInterface epi = (EditPageInterface) pageForConfigurationNamed(pageConfigurationName, session);
	 epi.setNextPageDelegate(this);
	 epi.setObject(enterpriseObject);
	 return epi;
}
protected WOComponent pageForConfigurationNamed(String pageConfigurationName, Session session) {
	 WOComponent component = D2W.factory().pageForConfigurationNamed(pageConfigurationName, session);
	 if (component instanceof D2WPage) {
	 	 ((D2WPage) component).setNextPageDelegate(this);
	 }
	 return component;
}
Summary
• Re-entrant controller provides code reusability
• Specify branch choices programmatically
• Storing the editing context allows easy access to associated
objects during flows and encourages a single point of save
• Utility and convenience methods allow for simple setup and
development of customized flows
Resources
• What is Direct To Web?
http://wiki.wocommunity.org/pages/viewpage.action?pageId=1049018
• D2W Flow Control
http://wiki.wocommunity.org/display/documentation/D2W+Flow+Control
• ERDBranchDelegateInterface
http://jenkins.wocommunity.org/job/Wonder/lastSuccessfulBuild/javadoc/er/directtoweb/pages/
ERD2WPage.html#pageController()
• Page controller in ERD2W
http://osdir.com/ml/web.webobjects.wonder-disc/2006-05/msg00041.html
Q&A

D2W Stateful Controllers

  • 1.
    D2W Stateful Controllers FuegoDigital Media QSTP-LLC Daniel Roy
  • 2.
    • Using WebObjectssince 2004 • Work with government agencies, private companies and various foundations globally • Fuego Content Management System & Fuego Frameworks • Presence in North America and Middle East Fuego History
  • 4.
    • 42% ofcommunity uses D2W • “D2W is to WebObjects apps as the assembly line is to automobiles...you provide customization directions (D2W rules) to the assembly line (D2W) to build your end products (WOComponent pages).” • Flow is controlled by delegates that manage the actions and direction of the application • Use ERDBranchDelegate to define the actions displayed on the page by rules or via introspection Direct To Web
  • 5.
  • 6.
    Start/Stop Confirm Edit Confirm Confirm saveForLater Save Delete edit Edit CommentreadyToSubmit* readyToSubmit* saveAndSubmit Submit delete* confirmDeletion approveDeletion confirm edit* List Expense Claims select cancel Notes: -Cancel is present on all pages, but only shown for first. - Any branches with * are optional and are shown based on business logic - Any branches with the same name, are the same page configuration, but with different branches Needs to enter comment? NO Edit Comment YES WHAT? WHAT??? ??
  • 7.
    Back in theday... • Without Project Wonder, rely on nextPageDelegate and nextPage for navigation • nextPageDelegate is checked first, and if found call nextPage() • without nextPageDelegate, nextPage() is called on the D2WPage component directly • Single class between each page
  • 8.
  • 9.
    Plain WebObjects ControllerSample Code public class ManageUserEditNextPageDelegate implements NextPageDelegate { public WOComponent nextPage(WOComponent sender) { NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects(); User currentUser = selectedObjects.get(0); EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session()); epi.setNextPageDelegate(new ManageUserSaveNextPageDelegate()); epi.setObject(currentUser); return (WOComponent) epi; } }
  • 10.
    Enter Project Wonder •ERDBranchDelegate to the rescue! • Now you can show multiple branch choices for a page • Pull choices from D2W context in the method branchChoicesForContext(), using the D2W rule key “branchChoices” • Or, use introspection to find all methods that take a single WOComponent as parameter
  • 11.
  • 12.
    ERDBranchDelegate Sample Code publicclass ManageUserControllerSelectBranchDelegate extends ERDBranchDelegate { // Return an edit page for a single selected row in the pick page public WOComponent edit(WOComponent sender) { NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects(); User currentUser = selectedObjects.get(0); EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session()); epi.setNextPageDelegate(new ManageUserControllerSaveBranchDelegate()); epi.setObject(currentUser); return (WOComponent) epi; } // From the selected items in the pick page, perform a delete public WOComponent delete(WOComponent sender) { NSArray<User> users = ((ERDPickPageInterface) sender).selectedObjects(); for (User user : users) { user.delete(); } if (users.count() > 0) { // Assuming all in same EC. users.get(0).editingContext().saveChanges(); } return sender.pageWithName("PageWrapper"); } }
  • 13.
    But wait... • Muststill set and get the objects on each page • Lots of classes to write • Why not reuse the same delegate?
  • 14.
    Benefits of DelegateReuse • Enter the controller/delegate many times (one controller for many pages) • Reuse the stored state in editing context • Support one or many flows within the single class • Define the visible branches using rules
  • 15.
  • 16.
    Is ERDBranchDelegate Enough? •Yes....but...no. • Branch choices are defined in the rules, but can lead to a complex ruleset depending on the business logic • Introspection on the ERDBranchDelegate returns all the methods in the class
  • 17.
    Hello (Stateful) World! •FDStatefulController is a re-entrant branch delegate extending ERDBranchDelegate • Better separation of MVC • Override branchChoicesForContext(D2WContext context) • Reuse page configurations (define the PC in code) • store state between pages • editing context • EOs, etc in subclasses
  • 18.
    branchChoicesForContext public class StatefulControllerextends ERDBranchDelegate { private NSArray<?> branches; private EOEditingContext editingContext; public NSArray<?> getBranches() { return branches; } public void setBranches(NSArray<?> branches) { this.branches = branches; } public EOEditingContext editingContext() { if (this.editingContext == null) { this.editingContext = ERXEC.newEditingContext(); } return this.editingContext; } public NSArray branchChoicesForContext(D2WContext context) { NSArray choices = getBranches(); if (choices == null) { branches = (NSArray) context.valueForKey(BRANCH_CHOICES); } else { NSMutableArray translatedChoices = new NSMutableArray(); for (Iterator iter = choices.iterator(); iter.hasNext();) { ... rest of method continues the same as ERDBranchDelegate
  • 19.
    StatefulController Code // Returnan edit page for a single selected row in the pick page public WOComponent edit(WOComponent sender) { NSArray<User> selectedObjects = ((ERDPickPageInterface) sender).selectedObjects(); User currentUser = selectedObjects.get(0); setBranches(new NSArray("save")); EditPageInterface epi = (EditPageInterface) D2W.factory().pageForConfigurationNamed("ManageUsers_User_Edit_PC", sender.session()); epi.setNextPageDelegate(this); epi.setObject(currentUser); return (WOComponent) epi; } public WOComponent save(WOComponent sender) { editingContext().saveChanges(); return sender.pageWithName("PageWrapper"); } // From the selected items in the pick page, perform a delete public WOComponent delete(WOComponent sender) { setBranches(null); NSArray<User> users = ((ERDPickPageInterface) sender).selectedObjects(); ERXUtilities.deleteObjects(editingContext(), users); return save(sender); }
  • 20.
  • 21.
    Utility Method Examples protectedEditPageInterface editPage(Session session, String pageConfigurationName, EOEnterpriseObject enterpriseObject) { EditPageInterface epi = (EditPageInterface) pageForConfigurationNamed(pageConfigurationName, session); epi.setNextPageDelegate(this); epi.setObject(enterpriseObject); return epi; } protected WOComponent pageForConfigurationNamed(String pageConfigurationName, Session session) { WOComponent component = D2W.factory().pageForConfigurationNamed(pageConfigurationName, session); if (component instanceof D2WPage) { ((D2WPage) component).setNextPageDelegate(this); } return component; }
  • 23.
    Summary • Re-entrant controllerprovides code reusability • Specify branch choices programmatically • Storing the editing context allows easy access to associated objects during flows and encourages a single point of save • Utility and convenience methods allow for simple setup and development of customized flows
  • 24.
    Resources • What isDirect To Web? http://wiki.wocommunity.org/pages/viewpage.action?pageId=1049018 • D2W Flow Control http://wiki.wocommunity.org/display/documentation/D2W+Flow+Control • ERDBranchDelegateInterface http://jenkins.wocommunity.org/job/Wonder/lastSuccessfulBuild/javadoc/er/directtoweb/pages/ ERD2WPage.html#pageController() • Page controller in ERD2W http://osdir.com/ml/web.webobjects.wonder-disc/2006-05/msg00041.html
  • 25.