Less Hacking, More Configuring - or - Adopting uPortal, not Adapting uPortal
Abstract |
---|
This discussion focuses on how the JA-SIG uPortal developer community can change |
I would like to get to the point where I can download JA-SIG uPortal,
build the WAR, reconfigure a new a new JNDI resources, and add a few
of my JARs to produce Luminis Platform. Being able to do this will
allow me to keep up-to-date with JA-SIG uPortal more easily and,
conversely, will allow me to make more timely contributions to
JA-SIG uPortal.
Although the Release Strategy has raised awareness and improved some
of these issues, I really can't do this today because contributions
continually introduce binary incompatibiltities, schema
incompatibilities, and lack migration paths.
Understanding the value and differences between binary compatibility,
schema compatibility, and data migration as different upgrade paths.
Original implementation:
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); public String getHelloWorld() { return dao.getString("helloWorld"); } public String setHelloWorld(String value) { return dao.setString("helloWorld", value); } }
Client code (separate compilation unit):
public class MyClient { public String getHelloWorld() { return new SomeClass().getHelloWorld(); } }
Trivial and unnecessary update which causes a binary incompatibility
with MyClient.
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); public String getGeeting() { return dao.getString("helloWorld"); } public String setGreeting(String value) { return dao.setString("helloWorld", value); } }
Fix to address the binary incompatibility.
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); /** * @deprecated use getGreeting **/ public String getHelloWorld() { return getGeeting(); } /** * @deprecated use setGreeting **/ public String setHelloWorld(String value) { return dao.setString("helloWorld", value); } public String getGeeting() { return dao.getString("helloWorld"); } public String setGreeting(String value) { return dao.setString("helloWorld", value); } }
A trivial schema change which causes a schema incompatibility, and
which likely will cause a runtime problem:
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); /** * @deprecated use getGreeting **/ public String getHelloWorld() { return getGeeting(); } /** * @deprecated use setGreeting **/ public String setHelloWorld(String value) { return setGreeting(value); } public String getGeeting() { return dao.getString("greeting"); } public String setGreeting(String value) { return dao.setString("greeting", value); } }
A fix for the schema change and doing in-place data migration:
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); /** * @deprecated use getGreeting **/ public String getHelloWorld() { String helloWorld = dao.getString("helloWorld"); if( null != helloWorld ) { // If data is not null, migrate data to new schema. dao.setString( "greeting", helloWorld ); dao.setString( "helloWorld", null ); } else { helloWorld = getGreeting(); } return helloWorld; } /** * @deprecated use setGreeting **/ public String setHelloWorld(String value) { if( null != dao.getString("helloWorld") ) { // If data has not been migrated yet, add dao.setString("helloWorld",value); } else { setGreeting(value); } } public String getGeeting() { return dao.getString("greeting"); } public String setGreeting(String value) { return dao.setString("greeting", value); } }
When does Luminis Platform have to preserve binary compatibility? When
SomeClass and MyClient are in different binary distributions the cost of migration
becomes difficult or impossible to calculate. If the two are in the
same binary, then you only have to worry about schema compatibility.
If you only have to worry about schema compatibilty, you can clean-up
the code; just move the migration code to a separate binary
distribution:
public class SomeClass { private SomeClassDao dao = new SomeClassDao(); public String getGeeting() { return dao.getString("greeting"); } public String setGreeting(String value) { return dao.setString("greeting", value); } }
The migration code is simply:
public class SomeClassMigrator { private SomeClassDao dao = new SomeClassDao(); public String migrateHelloWorld() { String helloWorld = dao.getString("helloWorld"); if( null != helloWorld ) { dao.setString("greeting", helloWorld); } } }
The Luminis Platform uses ~99% migration code, and ~1% in-line
migration. We generally try to avoid migration whereever possible, and
we maintain binary compatibility for published SDKs with very few
exceptions, similar to the Java SDK.
Using the Spring Framework is a positive step toward our goal of configuring our way from
JA-SIG uPortal to Luminis Platform. It will allow us to perform much of the wiring we
do today in code far more flexibly in configuration files. While this flexibility does
come at some cost, we believe it's worth that cost for Luminis Platform.
To further out configurability goals, we would like to refactor the PropertiesManager
to have provider structure which will allow Luminis Platform to plugin our
configuration system beneath JA-SIG uPortal. If approved, we will
make this contribution after Luminis Platform IV commitments are
complete.