Migrating existing Projects to Wonder

66
Migrating existing Projects to Wonder Maik Musall, Selbstdenker AG Samstag, 22. Juni 13

Transcript of Migrating existing Projects to Wonder

Page 1: Migrating existing Projects to Wonder

Migrating existing Projects to WonderMaik Musall, Selbstdenker AG

Samstag, 22. Juni 13

Page 2: Migrating existing Projects to Wonder

• Existing application of some complexity

• Using perhaps custom frameworks, but not yet Wonder

• Want to use Wonder for several reasons, but uncertain where to start and how to manage the migration process

The Task

Samstag, 22. Juni 13

Page 3: Migrating existing Projects to Wonder

Goals

• Make the switch at a chosen, planned point in time

• Being able to switch back to the non-Wonder version if problems come up in production

• Identify which steps to do when, and how

Samstag, 22. Juni 13

Page 4: Migrating existing Projects to Wonder

The Big Steps

• Move to Git (if you haven‘t already)

• Prepare the code base ahead of the actual wonderization

• Create a wonderization branch

• Wonderize in that branch, periodically merge new stuff from your main branch

• Release and celebrate

Samstag, 22. Juni 13

Page 5: Migrating existing Projects to Wonder

Prep step: Move to Git

• You‘ll need branches during the migration

• Wonder is Git-based anyway

• It just makes everything easier

• Plan some time to get up to speed using Git first

• Use Sourcetree

Samstag, 22. Juni 13

Page 6: Migrating existing Projects to Wonder

Prep step: Move to Git

Time

release branches masterdevelop hot!xesfeature

branches

Feature for future

release

Tag

1.0

Major feature for

next release

From this point on, “next release”

means the release after 1.0

Severe bug !xed for

production:hot!x 0.2

Bug!xes from rel. branch

may be continuously merged back into develop

Tag

0.1

Tag

0.2

Incorporate bug!x in develop

Only bug!xes!

Start of release

branch for1.0

Author: Vincent DriessenOriginal blog post: http://nvie.com/archives/323License: Creative Commons

Samstag, 22. Juni 13

Page 7: Migrating existing Projects to Wonder

Prep step: Move to Git

Time

release branches masterdevelop hot!xesfeature

branches

Feature for future

release

Tag

1.0

Major feature for

next release

From this point on, “next release”

means the release after 1.0

Severe bug !xed for

production:hot!x 0.2

Bug!xes from rel. branch

may be continuously merged back into develop

Tag

0.1

Tag

0.2

Incorporate bug!x in develop

Only bug!xes!

Start of release

branch for1.0

Author: Vincent DriessenOriginal blog post: http://nvie.com/archives/323License: Creative Commons

Samstag, 22. Juni 13

Page 8: Migrating existing Projects to Wonder

Prep step: Move to Git

Time

release branches masterdevelop hot!xesfeature

branches

Feature for future

release

Tag

1.0

Major feature for

next release

From this point on, “next release”

means the release after 1.0

Severe bug !xed for

production:hot!x 0.2

Bug!xes from rel. branch

may be continuously merged back into develop

Tag

0.1

Tag

0.2

Incorporate bug!x in develop

Only bug!xes!

Start of release

branch for1.0

Author: Vincent DriessenOriginal blog post: http://nvie.com/archives/323License: Creative Commons

Samstag, 22. Juni 13

Page 9: Migrating existing Projects to Wonder

Prep step: Move to Git

Time

release branches masterdevelop hot!xesfeature

branches

Feature for future

release

Tag

1.0

Major feature for

next release

From this point on, “next release”

means the release after 1.0

Severe bug !xed for

production:hot!x 0.2

Bug!xes from rel. branch

may be continuously merged back into develop

Tag

0.1

Tag

0.2

Incorporate bug!x in develop

Only bug!xes!

Start of release

branch for1.0

Author: Vincent DriessenOriginal blog post: http://nvie.com/archives/323License: Creative Commons

Samstag, 22. Juni 13

Page 10: Migrating existing Projects to Wonder

Managing Wonderization in Git

first wonderized release

second wonderized release

conventional version as fallback

conventional version as fallback

wonderized version merged into main

1.0

1.0

develop wonderize

1.1

feature

1.2

1.2

1.3

1.3

2.0

1.2

Samstag, 22. Juni 13

Page 11: Migrating existing Projects to Wonder

Prep step: Java packages

• You can‘t inherit from packaged classes if your classes aren‘t in packages, too

• So if you haven‘t already, create packages and move all your sources into them

• Benefit: clarified namespaces for your stuff

Samstag, 22. Juni 13

Page 12: Migrating existing Projects to Wonder

Java packages gotchas

• Getters and setters in components need to be(come) public

• Check class.getName() calls to become class.getSimpleName()

• Class.forName() needs full packaged path

• Overridden methods in enums become unreachable code in WO bindings

Samstag, 22. Juni 13

Page 13: Migrating existing Projects to Wonder

public enum ContentType {! literature {! ! @Override public String cssClassName() { return "read"; }! },! film {! ! @Override public String cssClassName() { return "watch"; }! },! music {! ! @Override public String cssClassName() { return "listen"; }! };!! public abstract String cssClassName();! public boolean isAvailable() { return true; }}

Packages: overriding enum methods

Samstag, 22. Juni 13

Page 14: Migrating existing Projects to Wonder

Packages: overriding enum methods

public enum ContentType {! literature {! ! @Override public String cssClassName() { return "read"; }! },! film {! ! @Override public String cssClassName() { return "watch"; }! },! music {! ! @Override public String cssClassName() { return "listen"; }! },! pr0n {! ! @Override public String cssClassName() {! ! ! return getUser().isAdult() ? "watch" : "nothingForYou";! ! }! };!! public abstract String cssClassName();}

Samstag, 22. Juni 13

Page 15: Migrating existing Projects to Wonder

Packages: overriding enum methods

public enum ContentType {! literature {! ! @Override String cssClassNameImpl() { return "read"; }! },! film {! ! @Override String cssClassNameImpl() { return "watch"; }! },! music {! ! @Override String cssClassNameImpl() { return "listen"; }! },! pr0n {! ! @Override String cssClassNameImpl() {! ! ! return getUser().isAdult() ? "watch" : "nothingForYou";! ! }! };!! abstract String cssClassNameImpl();! public String cssClassName() { return cssClassNameImpl(); }}

Samstag, 22. Juni 13

Page 16: Migrating existing Projects to Wonder

Packages: overriding enum methods

public enum ContentType implements NSKeyValueCoding {! literature {! ! @Override public String cssClassName() { return "read"; }! },! film {! ! @Override public String cssClassName() { return "watch"; }! },! music {! ! @Override public String cssClassName() { return "listen"; }! },! pr0n {! ! @Override public String cssClassName() {! ! ! return getUser().isAdult() ? "watch" : "nothingForYou";! ! }! };! !! public abstract String cssClassName();! @Override public void takeValueForKey( Object obj, String s ) { return; }! @Override public Object valueForKey( String s ) {! ! try {! ! ! return this.getClass().getMethod( s, (Class<?>[]) null ).invoke( this, (Object[]) null );! ! } catch( Exception e ) {! ! ! throw new RuntimeException( e );! ! }! }}

Samstag, 22. Juni 13

Page 17: Migrating existing Projects to Wonder

Prep step: own EC class

• You gain a lot of flexibility by using your own EOEditingContext subclass

• example: logging on saveChanges() or invalidateAllObjects()

• example: undoManager().removeAllActions() after saves

• Changing the superclass later to ERXEC becomes easy

Samstag, 22. Juni 13

Page 18: Migrating existing Projects to Wonder

Prep step: own DA class

• Create a common superclass between concrete DirectAction classes and WODirectAction

• Changing the superclass later to ERXDirectAction becomes easy

Samstag, 22. Juni 13

Page 19: Migrating existing Projects to Wonder

Prep step: own logging class

• Create your own org.apache.log4j.Logger subclass

• Changing the superclass later to ERXLogger becomes easy

Samstag, 22. Juni 13

Page 20: Migrating existing Projects to Wonder

import org.apache.log4j.Logger;

public class MyLogger extends Logger {

! public MyLogger( String name ) {! ! super( name );! }

! public static Factory factory = null;! static {! ! String factoryClassName = MyLogger.Factory.class.getName();! ! try {! ! ! MyLogger.factory = (Factory) Class.forName( factoryClassName ).newInstance();! ! } catch( Exception ex ) {! ! ! System.err.println( "Exception while creating logger factory of class " + factoryClassName + ": " + ex );! ! }! }

! public static class Factory implements org.apache.log4j.spi.LoggerFactory {! ! @Override! ! public Logger makeNewLoggerInstance( String name ) {! ! ! return new MyLogger( name );! ! }! ! public void loggingConfigurationDidChange() {! ! }! }

Your own logging class (1/2)

Samstag, 22. Juni 13

Page 21: Migrating existing Projects to Wonder

! public static MyLogger getMyLogger( String name ) {! ! Logger logger = MyLogger.getLogger( name );! ! if( logger != null && ! (logger instanceof MyLogger) ) {! ! ! throw new RuntimeException(! ! ! ! "Can't load Logger for \""! ! ! ! + name! ! ! ! + "\" because it is not of class MyLogger but \""! ! ! ! + logger.getClass().getName()! ! ! ! + "\". Check if there is a \"log4j.loggerFactory=er.extensions.Logger$Factory\" line in your properties."! ! ! );! ! }! ! return (MyLogger) logger;! }

! public static Logger getLogger( String name ) {! ! return Logger.getLogger( name, MyLogger.factory );! }

! public static MyLogger getMyLogger( Class clazz ) {! ! return MyLogger.getMyLogger( clazz.getName() );! }

! public static Logger getLogger( Class clazz ) {! ! return MyLogger.getMyLogger( clazz );! }

}

Your own logging class (2/2)

Samstag, 22. Juni 13

Page 22: Migrating existing Projects to Wonder

Prep step: rename enums

• ERXKey constants in new templates could collide with enum names

• Common collision pattern: uppercase enum with same name as EO attribute

• Eclipse refactoring tools are your friend

• Make this a separate commit

Samstag, 22. Juni 13

Page 23: Migrating existing Projects to Wonder

public class MyFlightRoute extends _MyFlightRoute {

! public static enum STATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }

! public void setStatus( STATUS status ) {! ! super.setStatus( status.name() );! }

}

enum renames

Samstag, 22. Juni 13

Page 24: Migrating existing Projects to Wonder

public class MyFlightRoute extends _MyFlightRoute {

! public static enum STATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }

! public void setStatus( STATUS status ) {! ! super.setStatus( status.name() );! }

}

public abstract class _MyFlightRoute extends MyEOGenericRecord {

! public static final ERXKey<String> STATUS = new ERXKey<String>("status");! public static final String STATUS_KEY = STATUS.key();

}

enum renames

Samstag, 22. Juni 13

Page 25: Migrating existing Projects to Wonder

public class MyFlightRoute extends _MyFlightRoute {

! public static enum STATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }

! public void setStatus( STATUS status ) {! ! super.setStatus( status.name() );! }

}

public abstract class _MyFlightRoute extends MyEOGenericRecord {

! public static final ERXKey<String> STATUS = new ERXKey<String>("status");! public static final String STATUS_KEY = STATUS.key();

}

enum renames

Samstag, 22. Juni 13

Page 26: Migrating existing Projects to Wonder

public class MyFlightRoute extends _MyFlightRoute {

! public static enum FRSTATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }

! public void setStatus( FRSTATUS status ) {! ! super.setStatus( status.name() );! }

}

public abstract class _MyFlightRoute extends MyEOGenericRecord {

! public static final ERXKey<String> STATUS = new ERXKey<String>("status");! public static final String STATUS_KEY = STATUS.key();

}

enum renames

Samstag, 22. Juni 13

Page 27: Migrating existing Projects to Wonder

Prep step: instance settings

•-XX:MaxPermSize=256m

Samstag, 22. Juni 13

Page 28: Migrating existing Projects to Wonder

Wonderization: Frameworks

• Now is the time to start the actual wonderization

• Start by the usual way to import Wonder into Eclipse

• Then add ERJars, ERExtensions, WOOgnl and Wonder‘s JavaWOExtensions to your project

• And ERPrototypes if you want to use them

• Remove log4j and potentially other jars that are contained in ERJars (check version compatibilities)

Samstag, 22. Juni 13

Page 29: Migrating existing Projects to Wonder

Properties file

• Wonder manages nearly all settings through Properties

• Live in file Resources/Properties

• You have to create at least a minimal file to start with

Samstag, 22. Juni 13

Page 30: Migrating existing Projects to Wonder

Properties file

# OGNLognl.active = trueognl.helperFunctions = trueognl.inlineBindings = trueognl.parseStandardTags = false

# Miscer.extensions.stackTrace.cleanup = truefile.encoding = UTF-8

# EOFer.extensions.ERXEC.safeLocking = trueer.extensions.ERXEC.useSharedEditingContext = falseer.extensions.ERXEnterpriseObject.applyRestrictingQualifierOnInsert = trueer.extensions.ERXRaiseOnMissingEditingContextDelegate = false

# Migrationser.migration.migrateAtStartup = trueer.migration.createTablesIfNecessary = trueer.migration.modelNames = MYMODELNAMEMYMODELNAME.MigrationClassPrefix=com.selbstdenker.foo.bar.migration.MYMODELNAME

Samstag, 22. Juni 13

Page 31: Migrating existing Projects to Wonder

Application.java

• Requirement: subclass ERXApplication

• If you subclassed a custom base class instead of WOApplication, you can either make that inherit ERXApplication, or copy the methods you need over to your Application class.

Samstag, 22. Juni 13

Page 32: Migrating existing Projects to Wonder

public class Application extends ERXApplication {

! public static void main( String[] argv ) {! ! ERXApplication.main( argv, Application.class );! }

Application.java (1/2)

Samstag, 22. Juni 13

Page 33: Migrating existing Projects to Wonder

public class Application extends ERXApplication {

! public static void main( String[] argv ) {! ! ERXApplication.main( argv, Application.class );! }

! public Application() {! ! WOMessage.setDefaultEncoding( "UTF-8" );! ! ERXMessageEncoding.setDefaultEncodingForAllLanguages( "UTF-8" );! ! // ...and whatever else you need to have here, but not more.! ! log.info( "######### Application startup complete #########" );! }

Application.java (1/2)

Samstag, 22. Juni 13

Page 34: Migrating existing Projects to Wonder

public class Application extends ERXApplication {

! public static void main( String[] argv ) {! ! ERXApplication.main( argv, Application.class );! }

! public Application() {! ! WOMessage.setDefaultEncoding( "UTF-8" );! ! ERXMessageEncoding.setDefaultEncodingForAllLanguages( "UTF-8" );! ! // ...and whatever else you need to have here, but not more.! ! log.info( "######### Application startup complete #########" );! }

! // everything that can be deferred better goes here instead! @Override public void didFinishLaunching() {! ! new ERXShutdownHook() {! ! ! @Override public void hook() {! ! ! ! // cleanup that needs to run when application is shut down! ! ! }! ! };

! ! // example for project-specific stuff! ! taskManager = new BackgroundTaskManager();! ! taskManager.newRecurringTask( new MySystemState.SystemStateUpdaterTask(), 60 );! ! super.didFinishLaunching();

! ! log.info( "######### post-startup sequence complete #########" );! }

Application.java (1/2)

Samstag, 22. Juni 13

Page 35: Migrating existing Projects to Wonder

public class Application extends ERXApplication {

! public static void main( String[] argv ) {! ! ERXApplication.main( argv, Application.class );! }

! public Application() {! ! WOMessage.setDefaultEncoding( "UTF-8" );! ! ERXMessageEncoding.setDefaultEncodingForAllLanguages( "UTF-8" );! ! // ...and whatever else you need to have here, but not more.! ! log.info( "######### Application startup complete #########" );! }

! // everything that can be deferred better goes here instead! @Override public void didFinishLaunching() {! ! new ERXShutdownHook() {! ! ! @Override public void hook() {! ! ! ! // cleanup that needs to run when application is shut down! ! ! }! ! };

! ! // example for project-specific stuff! ! taskManager = new BackgroundTaskManager();! ! taskManager.newRecurringTask( new MySystemState.SystemStateUpdaterTask(), 60 );! ! super.didFinishLaunching();

! ! log.info( "######### post-startup sequence complete #########" );! }

Application.java (1/2)

Samstag, 22. Juni 13

Page 36: Migrating existing Projects to Wonder

! @Override protected void migrationsWillRun( ERXMigrator migrator ) {! ! log.info( "Starting migrations" );! }!! @Override protected void migrationsDidRun( ERXMigrator migrator ) {! ! log.info( "Finished migrations" );! }

}

Application.java (2/2)

Samstag, 22. Juni 13

Page 37: Migrating existing Projects to Wonder

! // instead of using a Property, this switches gzip on/off based on system type! @Override public boolean responseCompressionEnabled() {! ! switch( systemType() ) {! ! ! case TESTING! ! : return true;! ! ! case DEVELOPMENT! : return true;

! ! default!! ! ! : return false; // gzip done by load balancer! ! }! }

! @Override protected void migrationsWillRun( ERXMigrator migrator ) {! ! log.info( "Starting migrations" );! }!! @Override protected void migrationsDidRun( ERXMigrator migrator ) {! ! log.info( "Finished migrations" );! }

}

Application.java (2/2)

Samstag, 22. Juni 13

Page 38: Migrating existing Projects to Wonder

! // instead of using a Property, this switches gzip on/off based on system type! @Override public boolean responseCompressionEnabled() {! ! switch( systemType() ) {! ! ! case TESTING! ! : return true;! ! ! case DEVELOPMENT! : return true;

! ! default!! ! ! : return false; // gzip done by load balancer! ! }! }

! // the default context logging for exceptions is a bit too bulky for my taste, so strip that down a bit! @Override public NSMutableDictionary extraInformationForExceptionInContext( Exception e, WOContext context ) {! ! NSMutableDictionary<String,Object> extraInfo = ERXRuntimeUtilities.informationForException( e );! ! // copy informatinForContext() from ERXApplication, override and strip down! ! extraInfo.addEntriesFromDictionary( informationForContext( context ) );! ! extraInfo.addEntriesFromDictionary( ERXRuntimeUtilities.informationForBundles() );! ! return extraInfo;! }

! @Override protected void migrationsWillRun( ERXMigrator migrator ) {! ! log.info( "Starting migrations" );! }!! @Override protected void migrationsDidRun( ERXMigrator migrator ) {! ! log.info( "Finished migrations" );! }

}

Application.java (2/2)

Samstag, 22. Juni 13

Page 39: Migrating existing Projects to Wonder

Session.java

• Requirement: subclass ERXSession

• No code to show, nothing special to adapt

• Except when you had used MultiECLockManager

• If you did and you want to switch to ERXEC autolocking, remove any code related to MultiECLockManager from your Session class

Samstag, 22. Juni 13

Page 40: Migrating existing Projects to Wonder

MyEditingContext.java

• Recommendation: subclass ERXEC

• You need to implement a factory

• You can have multiple factories, like one that produces autolocking contexts, and another for manual locking, without having different classes.

Samstag, 22. Juni 13

Page 41: Migrating existing Projects to Wonder

MyEOEditingContext.java

public class MyEOEditingContext extends ERXEC {

! private boolean doCommitLogging;

! /*! * Constructors and Factories! */! private static Factory _myAutoLockingFactory;! private static Factory _myManualLockingFactory;

! private static synchronized Factory myAutoLockingFactory() {! ! if( _myAutoLockingFactory == null ) {! ! ! _myAutoLockingFactory = new MyECAutoLockingFactory();! ! }! ! return _myAutoLockingFactory;! }

! private static synchronized Factory myManualLockingFactory() {! ! if( _myManualLockingFactory == null ) {! ! ! _myManualLockingFactory = new MyECManualLockingFactory();! ! }! ! return _myManualLockingFactory;! }

Samstag, 22. Juni 13

Page 42: Migrating existing Projects to Wonder

! public static class MyECAutoLockingFactory extends ERXEC.DefaultFactory {! ! @Override protected EOEditingContext _createEditingContext( EOObjectStore parent ) {! ! ! MyEOEditingContext ec = new MyEOEditingContext(! ! ! ! ! parent == null ? EOEditingContext.defaultParentObjectStore() : parent );! ! ! setDefaultDelegateOnEditingContext( ec );! ! ! return ec;! ! }! }

!! public static class MyECManualLockingFactory extends ERXEC.DefaultFactory {! ! @Override protected EOEditingContext _createEditingContext( EOObjectStore parent ) {! ! ! MyEOEditingContext ec = new MyEOEditingContext(! ! ! ! ! parent == null ? EOEditingContext.defaultParentObjectStore() : parent ) {! ! ! ! @Override public boolean useAutoLock() { return false; }! ! ! ! @Override public boolean coalesceAutoLocks() { return false; }! ! ! };! ! ! setDefaultDelegateOnEditingContext( ec );! ! ! return ec;! ! }! }

MyEOEditingContext.java

Samstag, 22. Juni 13

Page 43: Migrating existing Projects to Wonder

! public static MyEOEditingContext newAutoLockingEC() {! ! MyEOEditingContext newEC = (MyEOEditingContext) myAutoLockingFactory()._newEditingContext();! ! newEC.init();! ! return newEC;! }

! public static MyEOEditingContext newAutoLockingEC( EOObjectStore objectStore ) {! ! MyEOEditingContext newEC = (MyEOEditingContext) myAutoLockingFactory()._newEditingContext( objectStore );! ! newEC.init();! ! return newEC;! }

! public static MyEOEditingContext newManualLockingEC() {! ! MyEOEditingContext newEC = (MyEOEditingContext) myManualLockingFactory()._newEditingContext();! ! newEC.init();! ! return newEC;! }

! public static MyEOEditingContext newManualLockingEC( EOObjectStore objectStore ) {! ! MyEOEditingContext newEC = (MyEOEditingContext) myManualLockingFactory()._newEditingContext( objectStore );! ! newEC.init();! ! return newEC;! }

! private void init() {! ! doCommitLogging = true;! ! setRetainsRegisteredObjects( true );! // http://lists.apple.com/archives/webobjects-dev/2010/Nov/msg00009.html! }

MyEOEditingContext.java

Samstag, 22. Juni 13

Page 44: Migrating existing Projects to Wonder

! @Override public void saveChanges() {! ! StringBuilder buf = null;! ! if( doCommitLogging ) {! ! ! buf = new StringBuilder();! ! ! buf.append( "MyEC" ).append( useAutoLock() ? " autoLocking" : " manualLocking" ).append( " saveChanges()" );

! ! ! StackTraceElement[] stack = new Throwable().getStackTrace();! ! ! if( stack != null && stack.length > 1 )! buf.append( "; " ).append( stack[1].toString() );

! ! ! Collection<String> updatedClasses = setOfClassesFor( updatedObjects() );! ! ! Collection<String> insertedClasses = setOfClassesFor( insertedObjects() );! ! ! Collection<String> deletedClasses = setOfClassesFor( deletedObjects() );! ! ! if( insertedClasses.size() > 0 ) buf.append( "; ins: " ).append( Joiner.on(",").join( insertedClasses ) );! ! ! if( updatedClasses.size() > 0 ) buf.append( "; upd: " ).append( Joiner.on(",").join( updatedClasses ) );! ! ! if( deletedClasses.size() > 0 ) buf.append( "; del: " ).append( Joiner.on(",").join( deletedClasses ) );! ! }

! ! long ms1 = 0; if( buf != null ) ms1 = System.currentTimeMillis();! ! super.saveChanges();! ! long ms2 = 0; if( buf != null ) ms2 = System.currentTimeMillis();

! ! NSUndoManager undoManager = undoManager();! ! if( undoManager != null ) undoManager.removeAllActions();! ! long ms3 = 0; if( buf != null ) ms3 = System.currentTimeMillis();

! ! if( buf != null ) {! ! ! buf.append( "; times " ).append( ms2-ms1 ).append( " " ).append( ms3-ms2 );! ! ! log.info( buf.toString() );! ! }! }

MyEOEditingContext.java

Samstag, 22. Juni 13

Page 45: Migrating existing Projects to Wonder

! @Override public void saveChanges() {! ! StringBuilder buf = null;! ! if( doCommitLogging ) {! ! ! buf = new StringBuilder();! ! ! buf.append( "MyEC" ).append( useAutoLock() ? " autoLocking" : " manualLocking" ).append( " saveChanges()" );

! ! ! StackTraceElement[] stack = new Throwable().getStackTrace();! ! ! if( stack != null && stack.length > 1 )! buf.append( "; " ).append( stack[1].toString() );

! ! ! Collection<String> updatedClasses = setOfClassesFor( updatedObjects() );! ! ! Collection<String> insertedClasses = setOfClassesFor( insertedObjects() );! ! ! Collection<String> deletedClasses = setOfClassesFor( deletedObjects() );! ! ! if( insertedClasses.size() > 0 ) buf.append( "; ins: " ).append( Joiner.on(",").join( insertedClasses ) );! ! ! if( updatedClasses.size() > 0 ) buf.append( "; upd: " ).append( Joiner.on(",").join( updatedClasses ) );! ! ! if( deletedClasses.size() > 0 ) buf.append( "; del: " ).append( Joiner.on(",").join( deletedClasses ) );! ! }

! ! long ms1 = 0; if( buf != null ) ms1 = System.currentTimeMillis();! ! super.saveChanges();! ! long ms2 = 0; if( buf != null ) ms2 = System.currentTimeMillis();

! ! NSUndoManager undoManager = undoManager();! ! if( undoManager != null ) undoManager.removeAllActions();! ! long ms3 = 0; if( buf != null ) ms3 = System.currentTimeMillis();

! ! if( buf != null ) {! ! ! buf.append( "; times " ).append( ms2-ms1 ).append( " " ).append( ms3-ms2 );! ! ! log.info( buf.toString() );! ! }! }

MyEOEditingContext.java

MyEC autoLocking saveChanges(); com.selbstdenker.foo.bar.MyJourneyManager.createNewInvoice(MyJourneyManager.java:650); ins: MyCustomerClaimItem(3),MyCustomerLiability(1),MyCustomerClaim(1); upd: PDCJourney(1); times 171 1

Samstag, 22. Juni 13

Page 46: Migrating existing Projects to Wonder

! private Collection<String> setOfClassesFor( NSArray<EOEnterpriseObject> objects ) {! ! Multiset<String> classNames = HashMultiset.create();! ! for( EOEnterpriseObject eo : objects ) {! ! ! classNames.add( eo.getClass().getSimpleName() );! ! }! ! List<String> namesWithCount = Lists.newArrayList();! ! for( Multiset.Entry entry : classNames.entrySet() ) {! ! ! namesWithCount.add( entry.getElement() + "(" + entry.getCount() + ")" );! ! }! ! return namesWithCount;! }

! public void setLoggingOnCommit( boolean value ) {! ! this.doCommitLogging = value;! }

MyEOEditingContext.java

Samstag, 22. Juni 13

Page 47: Migrating existing Projects to Wonder

!

public void lockIfManualLocking() {! ! if( useAutoLock() ) return;! ! lock();! }

! public void unlockIfManualLocking() {! ! if( useAutoLock() ) return;! ! unlock();! }!! public void saveChangesWithLockIfNecessary() {! ! if( hasChanges() ) {! ! ! lockIfManualLocking();! ! ! try {! ! ! ! saveChanges();! ! ! } finally {! ! ! ! unlockIfManualLocking();! ! ! }! ! }! }!}

MyEOEditingContext.java

Samstag, 22. Juni 13

Page 48: Migrating existing Projects to Wonder

autolock vs. manual

• In general, autolocking is the right choice for almost everything

• Consider using manual locking for db-intensive background tasks

• Main autolocking tradeoff: looots of lock/unlock calls that could become a significant overhead, depending on what you‘re doing

Samstag, 22. Juni 13

Page 49: Migrating existing Projects to Wonder

! public static void deleteObsoleteFolderEntries() {! ! Thread thread = new Thread() {! ! ! @Override! ! ! public void run() {! ! ! ! log.info( "deleteObsoleteFolderEntries: starting" );! ! ! ! MyEOEditingContext ec = MyEOEditingContext.newManualLockingEC();! ! ! ! ec.lock();! ! ! ! try {! ! ! ! ! MyJourneyFolderEntry.deleteObsoleteEntries( ec );! ! ! ! ! ec.saveChanges();! ! ! ! } catch( Exception e ) {! ! ! ! ! ec.revert();! ! ! ! } finally {! ! ! ! ! ec.unlock();! ! ! ! }! ! ! ! log.info( "deleteObsoleteFolderEntries: completed" );! ! ! }! ! };! ! thread.start();! }

Manual locking case example

Samstag, 22. Juni 13

Page 50: Migrating existing Projects to Wonder

! public static void deleteObsoleteFolderEntries() {! ! Thread thread = new Thread() {! ! ! @Override! ! ! public void run() {! ! ! ! log.info( "deleteObsoleteFolderEntries: starting" );! ! ! ! MyEOEditingContext ec = MyEOEditingContext.newManualLockingEC();! ! ! ! ec.lockIfManualLocking();! ! ! ! try {! ! ! ! ! MyJourneyFolderEntry.deleteObsoleteEntries( ec );! ! ! ! ! ec.saveChanges();! ! ! ! } catch( Exception e ) {! ! ! ! ! ec.revert();! ! ! ! } finally {! ! ! ! ! ec.unlockIfManualLocking();! ! ! ! }! ! ! ! log.info( "deleteObsoleteFolderEntries: completed" );! ! ! }! ! };! ! thread.start();! }

Manual locking case example

Samstag, 22. Juni 13

Page 51: Migrating existing Projects to Wonder

ERXGenericRecord

• Requirement: subclass ERXGenericRecord

• With EOGenerator, simply change your template‘s superclass declaration and import statements

• Of course you can change them to your own MyGenericRecord class instead, and let that extend ERXGenericRecord

• If you had a delegate in your EC to do stuff before saves, you can now use ERXGenericRecord.willUpdate() and .willInsert() instead

Samstag, 22. Juni 13

Page 52: Migrating existing Projects to Wonder

EO Templates

• Use Wonder templates from WOLips

• There‘s a wiki page with all sorts of alternatives

• In any case, take one that defines proper ERXKey constants

Samstag, 22. Juni 13

Page 53: Migrating existing Projects to Wonder

ERXDirectAction

• Requirement: subclass ERXDirectAction

• As usual, you can make your own base class in between

Samstag, 22. Juni 13

Page 54: Migrating existing Projects to Wonder

You‘re done!

Samstag, 22. Juni 13

Page 55: Migrating existing Projects to Wonder

You‘re done!almost...

Samstag, 22. Juni 13

Page 56: Migrating existing Projects to Wonder

Suggestions to proceed

• Beautify your qualifiers

• Migrations

• Array operators and bindings

Samstag, 22. Juni 13

Page 57: Migrating existing Projects to Wonder

ERX*Qualifier, ERXKey and ERXQ

public class MyFlightRoute extends _MyFlightRoute {

! public static enum FRSTATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }

! public void setStatus( FRSTATUS status ) {! ! super.setStatus( status.name() );! }

}

public abstract class _MyFlightRoute extends MyEOGenericRecord {

! public static final ERXKey<String> STATUS = new ERXKey<String>("status");! public static final String STATUS_KEY = STATUS.key();

}

Samstag, 22. Juni 13

Page 58: Migrating existing Projects to Wonder

EOQualifier flightRouteValidQualifier =! ! MyFlightRoute.STATUS.eq( FRSTATUS.current.name() ).or(! ! MyFlightRoute.STATUS.eq( FRSTATUS.preliminary.name() ));

EOQualifier flightRouteValidQualifier = ERXQ.or(! ! MyFlightRoute.STATUS.eq( FRSTATUS.current.name() ),! ! MyFlightRoute.STATUS.eq( FRSTATUS.preliminary.name() ));

EOQualifier flightRouteValidQualifier = new EOOrQualifier( new NSArray( new Object[]{! new EOKeyValueQualifier( MyFlightRoute.STATUS_KEY, EOQualifier.QualifierOperatorEqual, FRSTATUS.current.name() ),! new EOKeyValueQualifier( MyFlightRoute.STATUS_KEY, EOQualifier.QualifierOperatorEqual, FRSTATUS.preliminary.name() )} ) );

EOQualifier flightRouteValidQualifier =! ! MyFlightRoute.STATUS.inObjects( FRSTATUS.current.name(), FRSTATUS.preliminary.name() );

ERX*Qualifier, ERXKey and ERXQ

Samstag, 22. Juni 13

Page 59: Migrating existing Projects to Wonder

public class MYMODELNAME17 extends MyMigration {

! @Override public void upgrade( EOEditingContext editingContext, ERXMigrationDatabase database ) throws Throwable {! ! ERXMigrationTable journeyTable = database.existingTableNamed( MyJourney.ENTITY_NAME );! ! journeyTable.newFlagBooleanColumn( MyJourney.MY_NEW_ATTR_KEY, ALLOWS_NULL );! !! ! String sql = "UPDATE MyJourney SET myNewAttr = false WHERE customerRef IN (...whatever...)";! ! NSLog.out.appendln( "Executing SQL: " + sql );! ! ERXJDBCUtilities.executeUpdate( database.adaptorChannel(), sql, true );

! ! MyUserRole specialRole = MyUserRole.newInEc( editingContext );! ! specialRole.setName( "Speziaaaal" );! ! specialRole.setRoleType( MyUserRole.ROLETYPE.special.name() );! }

}

package com.selbstdenker.foo.bar.migration;

import com.webobjects.eocontrol.EOEditingContext;import er.extensions.migration.ERXMigrationDatabase;import er.extensions.migration.ERXMigrationDatabase.Migration;

public abstract class MyMigration extends Migration {! @Override public void downgrade( EOEditingContext editingContext, ERXMigrationDatabase database ) throws Throwable {! ! // do nothing! }}

Migrations

Samstag, 22. Juni 13

Page 60: Migrating existing Projects to Wonder

JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}

Array operators and Bindings

Samstag, 22. Juni 13

Page 61: Migrating existing Projects to Wonder

<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>

JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}

Array operators and Bindings

Samstag, 22. Juni 13

Page 62: Migrating existing Projects to Wonder

// valid

<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>

JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}

Array operators and Bindings

Samstag, 22. Juni 13

Page 63: Migrating existing Projects to Wonder

// valid

<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>

JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}

Array operators and Bindings

Samstag, 22. Juni 13

Page 64: Migrating existing Projects to Wonder

// valid

<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>

JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}

Array operators and Bindings

Samstag, 22. Juni 13

Page 65: Migrating existing Projects to Wonder

Information sources

• Wiki page „Project Wonder Installation“

• Wiki page „Integrate Wonder into an Existing Application“

• Wiki page „EOGenerator Templates and Additions“

• Wiki page „UTF-8 Encoding Tips“

• projectlombok.org

Samstag, 22. Juni 13

Page 66: Migrating existing Projects to Wonder

Q&A

email: [email protected] and app.net: @maikm

Samstag, 22. Juni 13