This document discusses alternatives to using WebObjects for developing web applications. It summarizes the key aspects of a stack that could satisfy former WebObjects developers, including:
- Dependency injection frameworks like Spring and Google Guice that allow loose coupling between classes.
- HTML frameworks like Tapestry that are similar to WebObjects in allowing infinitely nestable page components.
- JAX-RS as a REST framework specification implemented by libraries like Jersey that maps HTTP requests to Java methods.
- Migrating from WebObjects by keeping its philosophies but rewriting code from scratch using these new frameworks, with tools to import existing data models and port components like DirectToWeb and DirectToJavaClient.
4. 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
13. 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
14. 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) { .. }
}
15. 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) { .. }
...
}
16. 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) { .. }
}
17. 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
18. 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) { .. }
}
19. 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
20. 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
22. DI Framework Choices
• All support annotations
• All are very capable
• Choice is driven by front-end technology (Tapestry for us)
23. 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
25. HTML Framework - Choices
• JSF
• Tapestry (aka “T5” for “Tapestry v.5”)
• Spring MVC
• Wicket, Seam, Click, Grails, many others...
26. 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
27. 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)
28. 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.)
29. 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>
30. 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>
32. 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
33. REST Framework Has Different
Requirements from HTML One
Possible to build on top of Tapestry/WO/etc.,
but is it a good idea?
34. 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)
36. 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
37. 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;
}
}
38. 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
41. 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
42. No Direct Replacement for:
• DirectToWeb (likely possible to implement on top of Tapestry)
• DirectToJavaClient (however 3-tier ORM is available, provided by
Cayenne ROP)
43. WOCommunity Can:
• Create WO-to-T5 template migration tools
• Port Wonder to Cayenne/T5
• Port ERRest to Jersey/Cayenne
• ...
44. We’ve gone from Objective C to Java
once, so we can do it again...