45
Life outside WO by Andrus Adamchik, ObjectStyle LLC

Life outside WO

Embed Size (px)

Citation preview

Page 1: Life outside WO

Life outside WOby Andrus Adamchik, ObjectStyle LLC

Page 2: Life outside WO

• 1998 - 2003 : WebObjects programmer

• 1998 - present : Java programmer

• 2001 - present : working on Cayenne

• Apache member, open source developer

• Owner of ObjectStyle LLC

About me

Page 3: Life outside WO

Life outside WO

Page 4: 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

Page 5: Life outside WO

What alternative stack would satisfy a WO developer?

Page 6: Life outside WO

WO Stack

Page 7: Life outside WO

Generic DI-Centric Stack

Page 8: Life outside WO

Our Stack

Page 9: Life outside WO

Why not JEE?

Page 10: Life outside WO

JEE provides us container, web app structure and core

HTTP APIs:Servlets (JSR-315)

JAX-RS - Jersey (aka JSR-311, aka REST)

Page 11: Life outside WO

Stack Parts

• Dependency Injection

• HTML Framework

• REST Framework

• Persistence

Page 12: Life outside WO

Dependency Injection

Page 13: Life outside WO

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

Page 14: Life outside WO

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) { .. }}

Page 15: Life outside WO

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) { .. } ... }

Page 16: Life outside WO

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) { .. }}

Page 17: Life outside WO

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

Page 18: Life outside WO

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) { .. }}

Page 19: Life outside WO

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

Page 20: Life outside WO

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

Page 21: Life outside WO

DI Framework Choices

• Spring

• Google Guice

• Apache Tapestry

• CDI

• (Cayenne DI)

Page 22: Life outside WO

DI Framework Choices

• All support annotations

• All are very capable

• Choice is driven by front-end technology (Tapestry for us)

Page 23: Life outside WO

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

Page 24: Life outside WO

HTML Framework

Page 25: Life outside WO

HTML Framework - Choices

• JSF

• Tapestry (aka “T5” for “Tapestry v.5”)

• Spring MVC

• Wicket, Seam, Click, Grails, many others...

Page 26: Life outside WO

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

Page 27: Life outside WO

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)

Page 28: Life outside WO

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.)

Page 29: Life outside WO

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>

Page 30: Life outside WO

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>

Page 31: Life outside WO

REST Framework

Page 32: Life outside WO

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

Page 33: Life outside WO

REST Framework Has Different Requirements from HTML One

Possible to build on top of Tapestry/WO/etc., but is it a good idea?

Page 34: Life outside WO

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)

Page 35: Life outside WO

JAX-RS (JSR-311)

My favorite JEE spec :)

Page 36: Life outside WO

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

Page 37: Life outside WO

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; }}

Page 38: Life outside WO

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

Page 39: Life outside WO

Persistence Framework ... we’ll discuss it later

Page 40: Life outside WO

Migration from WO

Page 41: Life outside 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

Page 42: Life outside WO

No Direct Replacement for:

• DirectToWeb (likely possible to implement on top of Tapestry)

• DirectToJavaClient (however 3-tier ORM is available, provided by Cayenne ROP)

Page 43: Life outside WO

WOCommunity Can:

• Create WO-to-T5 template migration tools

• Port Wonder to Cayenne/T5

• Port ERRest to Jersey/Cayenne

• ...

Page 44: Life outside WO

We’ve gone from Objective C to Java once, so we can do it again...