advanced component design with gwt - intertech...gwt recap • web application framework • written...
TRANSCRIPT
Advanced Component Design with GWT
John DubchakNovember 2007
It’s all about Me...deal with it!
• Application Architect
• 15+ years of experience in Java, C++ and .NOT
• GWT fanatic
• OO bigot
• I love clever code that abstracts away complexity
What we’ll cover• GWT Basic Review
• Components and Frameworks
• Discuss basic motivating design problems
• My Component Framework
• Deployment and Packaging
• Global Warming
• World Hunger
GWT Recap
• Web application framework
• Written in Java
• AJAX enabled
• Client-side Java code translated/compiled into Javascript
• RPC remoting accomplished using proprietary binary protocol (AJAX)
GWT Recap, Part II
• Contains abstractions for text entry, arbitrary HTML, labels, buttons, composite views
• Layouts and Widget display using Panels
• Events and Listeners can be “attached” to objects to provide event-handling semantics
• Applications are divided into Modules
• Novel Remoting framework
GWT Recap, Part III
• Default “Hello World!” from applicationCreator command line app:
final Button button = new Button("Click me");
final Label label = new Label();
button.addClickListener(new ClickListener() { public void onClick(Widget sender) { if (label.getText().equals("")) label.setText("Hello World!"); else label.setText(""); } }); RootPanel.get("slot1").add(button); RootPanel.get("slot2").add(label);
GWT Recap, Part IV
• How about a “real” example from Mail sample:
rightPanel.add(mailList);rightPanel.add(mailDetail);mailList.setWidth("100%");mailDetail.setWidth("100%");
DockPanel outer = new DockPanel();outer.add(topPanel, DockPanel.NORTH);outer.add(shortcuts, DockPanel.WEST);outer.add(rightPanel, DockPanel.CENTER);outer.setWidth("100%");
outer.setSpacing(4);outer.setCellWidth(rightPanel, "100%");
Window.addWindowResizeListener(this);
Window.enableScrolling(false);Window.setMargin("0px");
RootPanel.get().add(outer);
GWT Recap - Yes, still!
• Out of the box, provides basic elements to create real-world AJAX apps without writing Javascript
• Provides ample opportunity for smart Java developers to fill gaps with custom code
• Wasn’t designed to address all application architectural needs
GWT Development “Opportunities”
• GWT wasn’t designed to do everything
• What it does it does very well: AJAX-enabled, Java-to-Javascript basic UI Widget Library
• No notion of Applications, Enhanced Security or detailed data-to-view integration
Taking a Step Back
• If this is an “advanced” presentation, we need more buzzwords!!!
Components, What?
• Szyperski defines following criteria:
• Multiple-use
• Non-context-specific
• Composable with other components
• Encapsulated i.e., non-investigable through its interfaces
• A unit of independent deployment and versioning
• John says: “A self-contained, reusable, tested, unit of functionality that provides sufficient behavior so as to be contractually complete and deployable”
What can we agree on?
• Reusable bundles of functionality
• Self-contained and deployable
• General in nature, based on some form of abstraction
• Extensible, Polymorphic and Substitutable
Components, Why?• What are the advantages to using and
implementing components ?
• Logical separation of work
• Manageable chunks of abstraction
• Implementable
• Deployable
• Testable
Frameworks
• Frameworks are generally composed of one or more software patterns
• Act as Architectural blueprints designed to solve a particular class of problem: ORM for persistence, Spring MVC for webapps, JDBC for database access, EJB for distributed computing
Differences
• Differ in the level of abstraction that you model at
• Frameworks are coarser grained
• Components are about one thing: think Single Responsibility Principle, but with a larger scope than just a Class or basic Type
It’s all Composition
• Classes (and types) are the lowest level of abstraction in your modeling
• “Composition” based on the OO Concept, not the Composite pattern
• Composition of classes form a Pattern
• Patterns compose to form Frameworks
• Frameworks can be used (in part) to implement component architectures
A Design Paradigm
• Interfaces provide type substitutability
• Abstract Base Classes that implement interfaces, but leave details to sub-classes, provide reusable functionality that is common across concrete sub-types
• A common recurring “theme” in GAF
• What I call it: ProviderFactory-Provider Paradigm
What does it look like?
Now in Code
Provider provider = ProviderFactory.createProvider("someProviderName");
// Set some input data on provider, if needed
Data someData = provider.provideSomething();
// Do something with results: “someData”
A Real ExampleMessageProvider provider = MessageProviderFactory.createProvider(MessageProviderType.JMS);MessageServer server = (MessageServer) provider.createServiceEndPoint(MessageServiceType.QUEUE_SERVER);
MessageFactory factory = provider.createMessageFactory();
MessageConfiguration configuration = factory.createMessageConfiguration(createConfigurationAdapter());
server.setMessageConnector(factory.createMessageConnector(configuration, MessagingType.QUEUING));
server.setMessageConfiguration(configuration);
server.run();
OO Principles
• Provide the motivation for our design and coding efforts
• 11 Principles
• Martin’s Agile Software Development book explains them very well - buy a copy!
• Liskov Substitutability, Single Responsibility, Dependency Inversion, Release/Reuse Equivalency, Open/Closed
My Favorite, Liskov
What is wanted here is something like the following substitution property: If for each object O of type S
there is an object O2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when O is substituted for O2 then S is a
subtype of T.
The Intersection of Concepts
• Using sound OO Principles, how can we address the development challenges or “opportunities” presented with GWT?
GAF“Yet another Framework”
• Abstractions around common cross-cutting architectural concerns: Security, SSO, Service invocation patterns (RPC), Layout, theming, data sharing and intra-page communication using events
• Consistently applied paradigm
• Probably should have called it YAF
Goals and Objectives
• Consistency
• Reusability
• Testability
• Simplicity and Flexibility
• Decoupled Architectures
• “Leveraging the opportunities” (buzzwords)
GAF Components
• Designed to solve cross-cutting Architectural concerns
• Areas covered include Applications, MVC, Basic Security (Authenticaton and Authorization), Page Components, Layout, Themes, RPC Simplification and Domain Model data type mapping
Application
• SRP: Starting point for launching Application
• Basic “container” for Configuration and Main Page
Application UML
Application Type Hierarchy
D.I. SecureApplication
DefaultSecureApplication app = new DefaultSecureApplication();app.setSecurityManager(createSecurityManager());app.setLoginPage(createLogin());app.setMainPage(createMainPage());app.start();
How does it work?
• The “start” method is the catalyst to kicking the application off:
public void start() { if (getAuthenticationToken() == null || !getAuthenticationToken().isValid()) { if (getLoginPage() != null) { getLoginPage().setEventListener(this); getLoginPage().render(); } else { throw new InvalidSecureApplicationStateException(NO_LOGIN_CONFIG); } }}
Once you have an Application, what’s
next?...Pages
Understanding Pages
• Implemented as a GoF “Composite” Object
• Page is an abstract concept that may contain a Layout...or not
• Provides behavior related to rendering a “Page” given a “Display Context”
Display Context
• Provides a means to decouple a Page from the underlying display mechanics
• Remember, GWT uses Panels to display and layout it’s Widgets
• Similar to an AWT or Swing Graphics object: knows how to display “something”
DisplayContext UML
Page Type Hierarchy
Page Events
• Decouple Page and Workflow from overall application
• Implementation of the Observer pattern
• Notifies appropriate Listeners of interesting Page “events”
• Events include notification for Page changes and page flow changes: think a wizard dialog
SSO-style Login
• A secure application is composed of a LoginPage
• LoginPage is part of the Page Hierarchy - more on that later
• Login is a boolean process: you’re either logged in or it fails
• How?
LoginEventListener
• SRP: Provide notification of login success or login failure
The Login Page
• Specialized Page to provide Login functionality
• Extends AbstractPage and customizes data flow using an MVC abstraction
• Notifies appropriate Listener of login success or failure
Login Page, da Code!public void render() { if (loginDelegate != null) { if (!loginDelegate.isInitialized()) { loginDelegate.initialize(); } if (listener != null) { loginDelegate.addLoginEventListener(listener); } if (getDisplayContext() != null) { loginDelegate.renderView(getDisplayContext()); } }}
A LoginDelegate, huh?
• Actual login is forwarded to it
• Based on the Model-View-Controller pattern
• Implementation of a Controller
Model-View-Controller• A logical separation of User Interface
(View) and Data (Model)
• Model data is updated when changes occur in the View
• Views are notified of external changes to Model data
• Coordination between the View and Data occurs through a Controller
MVC, UML and TLA!
MVC Event Notification
• Occurs through loose coupling with event propagation
• Events are fired when the View changes
• Events are fired when the Model changes
• A Controller listens and responds to events and forces appropriate “updates”
Events - things of interest
• Implementation of the GoF Observer pattern
• Provides a level of indirection for intra-object communication
• Loosely coupled design
• Interested handlers register to receive events
Yes, there’s a hierarchy!
Event Interactions
• Register and Respond
public void initialize() { super.initialize(); getMailView().registerPageChangeListener(this); getMailView().registerFolderChangeListener(this);}
public void onPageChange(int newPageNum, int pageSize) { ServiceDefTarget endpoint = (ServiceDefTarget) mailService; String url = GWT.getModuleBaseURL() + MAIL_SERVLET_URL; endpoint.setServiceEntryPoint(url);
AsyncCallback callback = createCallback();
mailService.getMessagesByPage(createMessageProperties(), newPageNum, pageSize, callback);
}
Login MVC Example
• Wiring occurs through basic dependency injection:
LoginPage page = new LoginPage();page.setDisplayContext(new GWTDisplayContext(RootPanel.get()));page.setLoginDelegate(factory.createController(
ConsultantAppControllerFactory.LOGIN));
Login User Interface
Login View Source
protected void fireViewEvent(Object data) { for (Iterator iter = listeners.iterator(); iter.hasNext();) { ViewListener vl = (ViewListener) iter.next(); vl.onViewEvent(data); }}
onViewEventpublic void onViewEvent(Object data) { ServiceDefTarget endpoint = (ServiceDefTarget) loginService; String url = GWT.getModuleBaseURL() + "login"; endpoint.setServiceEntryPoint(url); AsyncCallback callback = new AsyncCallback() { public void onFailure(Throwable object) { Window.alert("Remote communication failure."); sendFailureNotification(null); } public void onSuccess(Object object) { if (object instanceof AuthenticationToken) { sendSuccessNotification((AuthenticationToken) object); } else { Window.alert("Invalid login credentials supplied or login attempt failed."); getView().refresh(); } } };
loginService.login(createDTO(), callback);}protected void sendSuccessNotification(AuthenticationToken token) { for (Iterator iter = listeners.iterator(); iter.hasNext();) { LoginEventListener listener = (LoginEventListener) iter.next(); listener.onLoginSuccess(token); }}
protected void sendFailureNotification(Credentials credentials) { for (Iterator iter = listeners.iterator(); iter.hasNext();) { LoginEventListener listener = (LoginEventListener) iter.next(); listener.onLoginFailure(credentials); }}
Is Anyone out there?(Who’s listening?)
• DefaultSecureApplication
• Applause dammit! - we’ve basically come full circle on a simple SSO in a very short period of time)
DSA Code Wrap-uppublic void start() { if (getAuthenticationToken() == null || !getAuthenticationToken().isValid()) { if (getLoginPage() != null) { getLoginPage().setEventListener(this); getLoginPage().render(); } else { throw new InvalidSecureApplicationStateException(NO_LOGIN_CONFIG); } }}
public void onLoginSuccess(AuthenticationToken token) { if (token != null && token.isValid()) { authToken = token; createConfiguration(); super.start(); }}
Detailed MVC Example
• Mail Application
• Consists of all of the abstractions we’ve talked about thus far
• Uses events almost exclusively for view, page changes and email message display
Traffic Cop: MailController
public MailController() { // Create Model super.setModel(ModelFactory.createMailModel()); getModel().addListener(this); // Create View super.setView(ViewFactory.createMailView()); getView().setModel(getModel());}
public void initialize() { super.initialize(); getMailView().registerPageChangeListener(this); getMailView().registerFolderChangeListener(this);
createInitialConfig();}
User Interface: MailView
public void refresh() { // Refresh Data, top-down topPanel.setUserName(getMailModel().getFirstName()); shortcuts.setMailAccount(getMailModel().getAccount()); shortcuts.setFolderNames(getMailModel().getFolderNames()); shortcuts.refresh(); mailList.setPagedMailData(getMailModel().getMailData()); mailList.setTotalPages(getMailModel().
getTotalPages(getMailModel().getCurrentFolderName()));mailList.setTotalMessages(getMailModel().
getTotalMessagesInFolder(getMailModel().getCurrentFolderName())); mailList.refresh();
resizeClient();}
Model Data: MailModel
private String folderName;private List mailData = new ArrayList();private MailConfig config;private UserPreferencesDTO preferences;
Data Provider: MailService
public MailConfig getMailConfig(MessageConnectionProperties cp) throws MailException { return getProvider().getMailConfig(cp);}
public List getMessagesByPage(MessageConnectionProperties cp, int pageNumber, int pageSize) throws MailException { return getProvider().getMessagesByPage(cp, pageNumber, pageSize);}
public List getMessages(MessageConnectionProperties cp) throws MailException { return getProvider().getMessages(cp);}
public List getMessages(MessageConnectionProperties cp, int page, int pageSize) throws MailException { return getProvider().getMessages(cp, page, pageSize);}
public Object getFromSession(String sessionKey) { return getThreadLocalRequest().getSession().getAttribute(sessionKey);}
public String getSessionId() { return getThreadLocalRequest().getSession().getId();}
public void setInSession(String sessionKey, Object object) { getThreadLocalRequest().getSession().setAttribute(sessionKey, object);}
Packaging• Modules in GWT are the basic unit of
abstraction (remember, from Part 1?...yeah, me neither)
• UI translated Source is required on the classpath
• Translated source must be available at run-time as well
• JAR files are acceptable, but source is included
GAF Packaging
• Uses Ant to compile and build JAR
• JAR file is imported by Sample Application in Hosted mode
• JAR contents look odd (include source)
• GAF has a Module XML defining basic layout
GAF Module XML
<module> <inherits name="com.google.gwt.user.User"/> <source path="client"/></module>
Future Directions
• More work on Security - custom Authorization classes
• Spring Integration
• Hibernate Integration is questionable
• Window effects: Drag ‘n Drop, Sizing, Wizards etc
• Integration with Spring MVC, Struts etc.
Will there be a GWT Part III?
• If you’re interested in another one, send either myself or Jeff an email
Demo