Migrating existing Projects to Wonder

941 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
941
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Migrating existing Projects to Wonder

  1. 1. Migrating existing Projects toWonderMaik Musall, Selbstdenker AGSamstag, 22. Juni 13
  2. 2. • Existing application of some complexity• Using perhaps custom frameworks, but not yet Wonder• Want to use Wonder for several reasons, but uncertain where tostart and how to manage the migration processThe TaskSamstag, 22. Juni 13
  3. 3. Goals• Make the switch at a chosen, planned point in time• Being able to switch back to the non-Wonder version ifproblems come up in production• Identify which steps to do when, and howSamstag, 22. Juni 13
  4. 4. 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 fromyour main branch• Release and celebrateSamstag, 22. Juni 13
  5. 5. 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 SourcetreeSamstag, 22. Juni 13
  6. 6. Prep step: Move to GitTimerelease branchesmasterdevelop hotfixesfeaturebranchesFeaturefor futurereleaseTag1.0Majorfeature fornext releaseFrom this point on,“next release”means the releaseafter 1.0Severe bugfixed forproduction:hotfix 0.2Bugfixes fromrel. branchmay becontinuouslymerged backinto developTag0.1Tag0.2Incorporatebugfix indevelopOnlybugfixes!Start ofreleasebranch for1.0Author: Vincent DriessenSamstag, 22. Juni 13
  7. 7. Prep step: Move to GitTimerelease branchesmasterdevelop hotfixesfeaturebranchesFeaturefor futurereleaseMajorfeature fornext releaseSevere bugfixed forproduction:hotfix 0.2Tag0.1Tag0.2Incorporatebugfix indevelopStart ofSamstag, 22. Juni 13
  8. 8. Prep step: Move to GitreleaseTag1.0From this point on,“next release”means the releaseafter 1.0Bugfixes fromrel. branchmay beTag0.2Incorporatebugfix indevelopOnlybugfixes!Start ofreleasebranch for1.0Samstag, 22. Juni 13
  9. 9. Prep step: Move to GitTag1.0From this point on,“next release”means the releaseafter 1.0Bugfixes fromrel. branchmay becontinuouslymerged backinto developOnlybugfixes!Start ofreleasebranch for1.0Samstag, 22. Juni 13
  10. 10. Managing Wonderization in Gitfirst wonderized releasesecond wonderized releaseconventional version as fallbackconventional version as fallbackwonderized version merged into main1.01.0develop wonderize1.1feature1.21.21.31.32.01.2Samstag, 22. Juni 13
  11. 11. Prep step: Java packages• You can‘t inherit from packaged classes if your classes aren‘t inpackages, too• So if you haven‘t already, create packages and move all yoursources into them• Benefit: clarified namespaces for your stuffSamstag, 22. Juni 13
  12. 12. 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 WObindingsSamstag, 22. Juni 13
  13. 13. 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 methodsSamstag, 22. Juni 13
  14. 14. Packages: overriding enum methodspublic 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
  15. 15. Packages: overriding enum methodspublic 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
  16. 16. Packages: overriding enum methodspublic 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
  17. 17. Prep step: own EC class• You gain a lot of flexibility by using your own EOEditingContextsubclass• example: logging on saveChanges() or invalidateAllObjects()• example: undoManager().removeAllActions() after saves• Changing the superclass later to ERXEC becomes easySamstag, 22. Juni 13
  18. 18. Prep step: own DA class• Create a common superclass between concrete DirectActionclasses and WODirectAction• Changing the superclass later to ERXDirectAction becomes easySamstag, 22. Juni 13
  19. 19. Prep step: own logging class• Create your own org.apache.log4j.Logger subclass• Changing the superclass later to ERXLogger becomes easySamstag, 22. Juni 13
  20. 20. 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
  21. 21. ! public static MyLogger getMyLogger( String name ) {! ! Logger logger = MyLogger.getLogger( name );! ! if( logger != null && ! (logger instanceof MyLogger) ) {! ! ! throw new RuntimeException(! ! ! ! "Cant 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
  22. 22. Prep step: rename enums• ERXKey constants in new templates could collide with enumnames• Common collision pattern: uppercase enum with same name asEO attribute• Eclipse refactoring tools are your friend• Make this a separate commitSamstag, 22. Juni 13
  23. 23. public class MyFlightRoute extends _MyFlightRoute {! public static enum STATUS {! ! obsolete,! ! current,! ! preliminary,! ! deleted;! }! public void setStatus( STATUS status ) {! ! super.setStatus( status.name() );! }}enum renamesSamstag, 22. Juni 13
  24. 24. 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 renamesSamstag, 22. Juni 13
  25. 25. 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 renamesSamstag, 22. Juni 13
  26. 26. 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 renamesSamstag, 22. Juni 13
  27. 27. Prep step: instance settings•-XX:MaxPermSize=256mSamstag, 22. Juni 13
  28. 28. 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‘sJavaWOExtensions to your project• And ERPrototypes if you want to use them• Remove log4j and potentially other jars that are contained inERJars (check version compatibilities)Samstag, 22. Juni 13
  29. 29. 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 withSamstag, 22. Juni 13
  30. 30. 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.MYMODELNAMESamstag, 22. Juni 13
  31. 31. Application.java• Requirement: subclass ERXApplication• If you subclassed a custom base class instead of WOApplication,you can either make that inherit ERXApplication, or copy themethods you need over to your Application class.Samstag, 22. Juni 13
  32. 32. public class Application extends ERXApplication {! public static void main( String[] argv ) {! ! ERXApplication.main( argv, Application.class );! }Application.java (1/2)Samstag, 22. Juni 13
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. ! @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
  37. 37. ! // 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
  38. 38. ! // 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
  39. 39. 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 yourSession classSamstag, 22. Juni 13
  40. 40. MyEditingContext.java• Recommendation: subclass ERXEC• You need to implement a factory• You can have multiple factories, like one that producesautolocking contexts, and another for manual locking, withouthaving different classes.Samstag, 22. Juni 13
  41. 41. MyEOEditingContext.javapublic 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
  42. 42. ! 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.javaSamstag, 22. Juni 13
  43. 43. ! 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.javaSamstag, 22. Juni 13
  44. 44. ! @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.javaSamstag, 22. Juni 13
  45. 45. ! @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.javaMyEC autoLocking saveChanges();com.selbstdenker.foo.bar.MyJourneyManager.createNewInvoice(MyJourneyManager.java:650);ins: MyCustomerClaimItem(3),MyCustomerLiability(1),MyCustomerClaim(1);upd: PDCJourney(1);times 171 1Samstag, 22. Juni 13
  46. 46. ! 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.javaSamstag, 22. Juni 13
  47. 47. !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.javaSamstag, 22. Juni 13
  48. 48. 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 couldbecome a significant overhead, depending on what you‘re doingSamstag, 22. Juni 13
  49. 49. ! 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 exampleSamstag, 22. Juni 13
  50. 50. ! 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 exampleSamstag, 22. Juni 13
  51. 51. ERXGenericRecord• Requirement: subclass ERXGenericRecord• With EOGenerator, simply change your template‘s superclassdeclaration and import statements• Of course you can change them to your own MyGenericRecordclass instead, and let that extend ERXGenericRecord• If you had a delegate in your EC to do stuff before saves, you cannow use ERXGenericRecord.willUpdate() and .willInsert()insteadSamstag, 22. Juni 13
  52. 52. 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 constantsSamstag, 22. Juni 13
  53. 53. ERXDirectAction• Requirement: subclass ERXDirectAction• As usual, you can make your own base class in betweenSamstag, 22. Juni 13
  54. 54. You‘re done!Samstag, 22. Juni 13
  55. 55. You‘re done!almost...Samstag, 22. Juni 13
  56. 56. Suggestions to proceed• Beautify your qualifiers• Migrations• Array operators and bindingsSamstag, 22. Juni 13
  57. 57. ERX*Qualifier, ERXKey and ERXQpublic 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
  58. 58. 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 ERXQSamstag, 22. Juni 13
  59. 59. 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! }}MigrationsSamstag, 22. Juni 13
  60. 60. JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}Array operators and BindingsSamstag, 22. Juni 13
  61. 61. <wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}Array operators and BindingsSamstag, 22. Juni 13
  62. 62. // valid<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}Array operators and BindingsSamstag, 22. Juni 13
  63. 63. // valid<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}Array operators and BindingsSamstag, 22. Juni 13
  64. 64. // valid<wo:if condition = "$selectedJourney.passengerArray.@isEmpty">! <p>blah</p></wo:if>JourneyElementRepetition : WORepetition {! list = selectedJourney.passengerArray.@sortDesc.sortKeyConsideringStatus;! item = aPassenger;}Array operators and BindingsSamstag, 22. Juni 13
  65. 65. 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.orgSamstag, 22. Juni 13
  66. 66. Q&Aemail: maik@selbstdenker.agtwitter and app.net: @maikmSamstag, 22. Juni 13

×