Upload
wo-community
View
544
Download
2
Embed Size (px)
Citation preview
Life outside WOby Andrus Adamchik, ObjectStyle LLC
• 1998 - 2003 : WebObjects programmer
• 1998 - present : Java programmer
• 2001 - present : working on Cayenne
• Apache member, open source developer
• Owner of ObjectStyle LLC
About me
Life outside WO
Why Bother?
• WO is no longer a product sold by Apple
• There are lots of good technologies out there
• True open source / freedom
• Effort put in Wonder will have a much higher ROI
What alternative stack would satisfy a WO developer?
WO Stack
Generic DI-Centric Stack
Our Stack
Why not JEE?
JEE provides us container, web app structure and core
HTTP APIs:Servlets (JSR-315)
JAX-RS - Jersey (aka JSR-311, aka REST)
Stack Parts
• Dependency Injection
• HTML Framework
• REST Framework
• Persistence
Dependency Injection
Dependency Injection
• Frees dependency users from concerns over dependency initialization, scoping and lookup
• Loose coupling (aka “program to interfaces” style)
• Services as facades to complex third-party frameworks
• Makes services testable
DI Alternatives - Static Methods
public class MyPage { void setupRender() { // no DI, use a static utility String appName = PropertyUtils.getProperty(“app.name”); ... }}
public class PropertyUtils { private static ObjectContext context;
// who and when invokes this? public static void init(ObjectContext context) { PropertyUtils.context = context; } public static String getProperty(String key) { return System.getProperty(key) != null ? System.getProperty(key) : getFromDB(key); } private static String getFromDB(String key) { .. }}
DI Alternatives - Self-initializing Singletons
public class MyPage { void setupRender() { // no DI, use a static singleton String appName = PropertyUtils.singleton().getProperty(“app.name”); ... }}public class PropertyUtils { private static PropertyUtils singleton;
public static PropertyUtils singleton() { // is this thread-safe? is Cayenne runtime ready by now? if(singleton == null) { ObjectContext context = CayenneUtils.getContext(); singleton = new PropertyUtils() } return singleton; }
private ObjectContext context;
public PropertyUtils(ObjectContext context) { this.context = context; } public static String getProperty(String key) { return System.getProperty(key) != null ? System.getProperty(key) : getFromDB(key); } private static String getFromDB(String key) { .. } ... }
Dependency Injection
public class MyPage { @Inject private PropertyService propertyService;
void setupRender() { String appName = propertyService.getString(“app.name”); }}public class PropertyService { private ObjectContext context;
public PropertyService(@Inject ObjectContext context) { this.context = context; } public String getProperty(String key) { return System.getProperty(key) != null ? System.getProperty(key) : getFromDB(key); } private static String getFromDB(String key) { .. }}
Dependency Injection
• Frees dependency users from concerns over dependency initialization, scoping and lookup
• Loose coupling (aka “program to interfaces” style)
• Services as facades to complex third-party frameworks
• Makes services testable
Dependency Injection
public class MyPage { @Inject private IPropertyService propertyService;
void setupRender() { String appName = propertyService.getString(“app.name”); }}public interface IPropertyService { public String getProperty(String key);}
public class DBPropertyService implements PropertyService { // copy our old PropertyService code here ...}
public class FilePropertyService implements PropertyService { public String getProperty(String key) { return System.getProperty(key) != null ? System.getProperty(key) : getFromFile(key); } private static String getFromFile(String key) { .. }}
Dependency Injection
• Frees dependency users from concerns over dependency initialization, scoping and lookup
• Loose coupling (aka “program to interfaces” style)
• Services as facades to complex third-party frameworks
• Makes services testable
Dependency Injection
• Frees dependency users from concerns over dependency initialization, scoping and lookup
• Loose coupling (aka “program to interfaces” style)
• Services as facades to complex third-party frameworks
• Makes services testable
DI Framework Choices
• Spring
• Google Guice
• Apache Tapestry
• CDI
• (Cayenne DI)
DI Framework Choices
• All support annotations
• All are very capable
• Choice is driven by front-end technology (Tapestry for us)
Tapestry DI
• Services assembly in the code (no XML)
• Out of the box injection into T5 pages and components
• Easy integration with Jersey (same services can be injected into REST resources)
• Supports HttpServletRequest/Response injection
• Supports multiple DI modules
HTML Framework
HTML Framework - Choices
• JSF
• Tapestry (aka “T5” for “Tapestry v.5”)
• Spring MVC
• Wicket, Seam, Click, Grails, many others...
Why Tapestry?
• The most natural choice for a WO developer:
• Page is made of infinitely nestable components
• Components get their values via bindings from parent
• Something like WOComponentContent used to be nearly impossible with competition
Beyond WO Similarities
• No common superclass of pages and components
• “Static” component hierarchy (WOSwitchComponent-like functionality works differently)
• Injection into pages and components
• No separation between pages and direct actions (pages can serve DA-like responses)
Beyond WO Similarities - 2
• AJAX support / zones (some controversy here)
• Template inheritance
• Different and fairly complicated page rendering lifecycle
• Probably more scaleable (better state management facilities)
• Lots of other good stuff and extension points (mixins, blocks, etc., etc.)
Tapestry - a simple dynamic page
Index.java:package com.objectstyle.demo.html.pages;import org.apache.tapestry5.annotations.Persist;
public class Index { @Persist private int clickCounter;
public String getText() { return "Hi! Clicked " + clickCounter + " time(s)"; } public void onActionFromClick() { clickCounter++; }}
Index.tml:<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"> <body> <h1>${text}</h1> <p><t:actionlink t:id="click">click me</t:actionlink></p> </body></html>
Tapestry - a simple page with a custom component
PageWrapper.tml:<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"> <head><title>${title}</title></head> <body><t:body /></body></html>
PageWrapper.java:package com.objectstyle.demo.html.components;
import org.apache.tapestry5.annotations.Parameter;import org.apache.tapestry5.annotations.Property;
public class PageWrapper { @Parameter @Property private String title;}
Index.tml:<html t:type="pagewrapper" title="prop:text" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"> <h1>${text}</h1> <p><t:actionlink t:id="click">click me</t:actionlink></p></html>
REST Framework
REST Framework Has Different Requirements from HTML One
• Easy mapping of HTTP request parts to Java code
• HTTP method
• URL (path, parameters)
• Session state management is non-essential
• Responses are data structures without presentation elements
REST Framework Has Different Requirements from HTML One
Possible to build on top of Tapestry/WO/etc., but is it a good idea?
REST Framework - Choices
• Fortunately (?) fewer choices than with HTML frameworks
• Most based on JAX-RS spec (JSR-311):
• Jersey, Resteasy, Apache CXF
• No prominent higher-level frameworks yet (I am building a closed source one at the moment)
JAX-RS (JSR-311)
My favorite JEE spec :)
JAX-RS (JSR-311)
• Exposes POJO classes as web “resources”
• Annotation-based
• Usually deployed as a servlet
• (Like servlet spec) low-level enough to be close to HTTP protocol
• (Unlike servlet spec) very usable on its own to support many REST development scenarios without higher abstractions
JAX-RS - a simple resource
HelloResource.java:
@Path("rest")public class HelloResource { @GET @Produces(MediaType.APPLICATION_JSON) public Object sayHi() { return new Model("Hi!"); }}
Model.java:
class Model { private String say;
Model(String say) { this.say = say; } public String getSay() { return say; }}
JAX-RS provider - Jersey
• A reference implementation of JSR-311
• Support for REST client
• A unit test framework
• Trivial to integrate resource injection with Tapestry DI
• Happily coexists with T5 pages in the same app
Persistence Framework ... we’ll discuss it later
Migration from WO
What does it take?
• Keep the WO philosophy, but not the WO code
• Write the code from scratch
• Should be easy for new projects
• No 1-click migration for existing projects
• Start by using WO frontend with Cayenne (?)
• CayenneModeler / ERCayenne will import most EOModels
No Direct Replacement for:
• DirectToWeb (likely possible to implement on top of Tapestry)
• DirectToJavaClient (however 3-tier ORM is available, provided by Cayenne ROP)
WOCommunity Can:
• Create WO-to-T5 template migration tools
• Port Wonder to Cayenne/T5
• Port ERRest to Jersey/Cayenne
• ...
We’ve gone from Objective C to Java once, so we can do it again...